#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
待办事项管理软件
作者：拉轰真人
版本：V3.2.0

功能说明：
- 左右分栏界面，支持添加/编辑/删除待办
- 可锁定到桌面底层，防止误操作
- 用SQLite存数据，界面为圆角设计
- 带日期时间功能，支持右键编辑
- 记忆窗口位置和锁定状态
"""

import sys
import os
import sqlite3
import datetime
import winreg
import json
import hashlib
import subprocess
import urllib.request
import urllib.error
import tempfile
import shutil
from embedded_icons import get_icon

# 版本信息
APP_VERSION = "V3.2.0"
COPYRIGHT_INFO = "吾爱破解 [2025] @拉轰真人"

# 更新相关常量
UPDATE_URL = "https://gitee.com/tanbaolong1978/to-do-management---ai-version/raw/master/version.json"

# PyQt5相关导入
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget,
                            QVBoxLayout, QHBoxLayout, QPushButton, QLineEdit,
                            QListWidget, QListWidgetItem, QLabel, QSplitter,
                            QCheckBox, QMessageBox, QInputDialog, QMenu,
                            QDialog, QDateTimeEdit, QFormLayout,
                            QGraphicsDropShadowEffect, QTextEdit,
                            QDialogButtonBox, QFrame, QSizePolicy,
                            QToolTip, QStackedWidget, QSystemTrayIcon, QAction,
                            QGroupBox, QComboBox, QRadioButton, QButtonGroup, QScrollArea,
                            QFileDialog, QFontDialog)

from PyQt5.QtCore import (Qt, QSize, pyqtSlot, QDateTime,
                         QSettings, QPoint, QRect, QRectF, QTimer,
                         QPropertyAnimation, QEasingCurve, QThread, pyqtSignal)

from PyQt5.QtGui import (QIcon, QFont, QColor, QPalette, QBrush,
                        QLinearGradient, QCursor, QPainter, QPen,
                        QPainterPath, QPixmap)
from PyQt5.QtGui import QRegion

# 导入自定义模块
from webdav_sync import WebDAVSettingsDialog, get_machine_code
from search_plugin import open_search_dialog
from login import LoginDialog

# 插件相关导入
import json
from datetime import datetime

def get_todo_height_setting():
    """获取待办信息高度设置"""
    from PyQt5.QtCore import QSettings
    settings = QSettings("TodoApp", "WebDAVSync")
    height_setting = settings.value('todo_height', '默认高度')
    return height_setting

def get_app_dir():
    """获取应用程序目录，兼容开发和打包环境"""
    if getattr(sys, 'frozen', False):
        # 打包后的环境
        return os.path.dirname(sys.executable)
    else:
        # 开发环境
        return os.path.dirname(os.path.abspath(__file__))

def get_config_file_path():
    """获取配置文件的完整路径"""
    app_dir = get_app_dir()
    return os.path.join(app_dir, "plugin_config.json")

def get_icon_path():
    """获取图标路径，兼容开发和打包环境"""
    app_dir = get_app_dir()
    icon_path = os.path.join(app_dir, "icon.ico")
    if os.path.exists(icon_path):
        return icon_path
    
    if getattr(sys, 'frozen', False):
        # 打包后的环境，尝试_MEIPASS路径
        base_path = getattr(sys, '_MEIPASS', app_dir)
        icon_path = os.path.join(base_path, "icon.ico")
        if os.path.exists(icon_path):
            return icon_path
    else:
        # 开发环境，尝试当前工作目录
        work_dir = os.getcwd()
        icon_path = os.path.join(work_dir, "icon.ico")
        if os.path.exists(icon_path):
            return icon_path
    
    return None

def get_svg_icon_path(icon_name):
    """获取SVG图标路径"""
    app_dir = get_app_dir()
    icon_path = os.path.join(app_dir, "icons", f"{icon_name}.svg")
    if os.path.exists(icon_path):
        return icon_path
    return None

def get_embedded_svg_icon(icon_name):
    """从嵌入的SVG字符串创建QIcon对象"""
    svg_content = get_icon(icon_name)
    if svg_content:
        # 将SVG内容转换为字节
        svg_bytes = svg_content.encode('utf-8')
        
        # 创建QIcon对象
        icon = QIcon()
        pixmap = QPixmap()
        
        # 从SVG字节数据创建pixmap
        if pixmap.loadFromData(svg_bytes, 'SVG'):
            icon.addPixmap(pixmap)
            return icon
    
    return None

APP_ICON = get_icon_path()

# 颜色配置
COLORS = {
    'primary': '#4361ee',
    'secondary': '#4895ef', 
    'accent': '#3f37c9',
    'background': '#ffffff',
    'card_bg': '#f8f9fa',
    'text_primary': '#212529',
    'text_secondary': '#6c757d',
    'border': '#dee2e6',
    'success': '#4cc9f0',
    'warning': '#ff9e00',
    'error': '#ef476f',
    'gradient_start': '#4cc9f0',
    'gradient_end': '#4361ee',
}

class PasswordManager:
    """密码管理类"""
    def __init__(self, lock_file="lock.json"):
        self.lock_file = lock_file
        self.password_hash = None
        self.load_password()
    
    def load_password(self):
        """加载密码哈希"""
        try:
            if os.path.exists(self.lock_file):
                with open(self.lock_file, 'r', encoding='utf-8') as f:
                    data = json.load(f)
                    self.password_hash = data.get('password_hash')
        except Exception as e:
            print(f"加载密码失败: {e}")
            self.password_hash = None
    
    def save_password(self, password_hash):
        """保存密码哈希"""
        try:
            with open(self.lock_file, 'w', encoding='utf-8') as f:
                json.dump({'password_hash': password_hash}, f, ensure_ascii=False, indent=2)
            self.password_hash = password_hash
        except Exception as e:
            print(f"保存密码失败: {e}")
    
    def has_password(self):
        """检查是否已设置密码"""
        return self.password_hash is not None
    
    def verify_password(self, password):
        """验证密码"""
        if not self.password_hash:
            return False
        return self.password_hash == self._hash_password(password)
    
    def _hash_password(self, password):
        """密码哈希"""
        return hashlib.sha256(password.encode('utf-8')).hexdigest()
    
    def set_password(self, password):
        """设置密码"""
        self.save_password(self._hash_password(password))

class TodoDetailDialog(QDialog):
    """待办详情对话框"""
    def __init__(self, parent=None, content="", datetime_str="", reminder_time=None, is_completed=False, 
                 row_index=-1, is_completed_list=False, due_date=None):
        super().__init__(parent)
        self.setWindowTitle(f"待办详情 {APP_VERSION}")
        
        # 动态调整窗口大小以适应内容
        self.adjustWindowSize(content)
        
        if APP_ICON and os.path.exists(APP_ICON):
            self.setWindowIcon(QIcon(APP_ICON))
        
        # 设置窗口属性
        self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint | Qt.WindowStaysOnTopHint)
        
        # 保存额外信息用于编辑
        self.row_index = row_index
        self.is_completed_list = is_completed_list
        self.parent_window = parent
        
        # 主布局
        layout = QVBoxLayout(self)
        layout.setSpacing(15)
        layout.setContentsMargins(20, 20, 20, 20)
        
        # 文本显示区域
        self.text_edit = QTextEdit()
        self.text_edit.setReadOnly(True)
        self.text_edit.setLineWrapMode(QTextEdit.WidgetWidth)  # 启用自动换行
        self.text_edit.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)  # 需要时显示滚动条
        
        status = "已完成" if is_completed else "待办"
        
        # 处理段落显示
        formatted_content = self.formatContentForParagraphs(content)
        
        html_content = f"""
        <div style="font-family:'Microsoft YaHei'; font-size:10pt; line-height:1.5;">
            <p style="margin: 8px 0;"><b>状态:</b> {status}</p>
            <div style="margin: 15px 0; padding: 10px;">
                {formatted_content}
            </div>
            <p style="margin: 23px 0;"><b>添加时间:</b> {datetime_str}</p>
        """
        if reminder_time:
            html_content += f'<p style="margin: 8px 0;"><b>提醒时间:</b> {reminder_time}</p>'
        if due_date:
            html_content += f'<p style="margin: 8px 0;"><b>提醒时间:</b> {due_date}</p>'
        html_content += "</div>"
        
        self.text_edit.setHtml(html_content)
        layout.addWidget(self.text_edit)
        
        # 按钮区域
        button_layout = QHBoxLayout()
        
        # 复制按钮
        copy_button = QPushButton("复制内容")
        copy_button.setMinimumWidth(80)
        copy_button.setMinimumHeight(30)
        copy_button.clicked.connect(self.copyContent)
        copy_button.setStyleSheet(f"""
            QPushButton {{
                min-height: 30px;
                min-width: 60px;
                padding: 0 15px;
                background-color: {COLORS['primary']};
                color: white;
                border: none;
                border-radius: 6px;
                font-weight: bold;
                font-family: 'Microsoft YaHei';
                font-size: 12px;
                text-align: center;
            }}
            QPushButton:hover {{
                background-color: {COLORS['accent']};
            }}
            QPushButton:pressed {{
                background-color: {COLORS['secondary']};
            }}
        """)
        
        # 编辑按钮
        edit_button = QPushButton("编辑")
        edit_button.setMinimumWidth(80)
        edit_button.setMinimumHeight(30)
        edit_button.clicked.connect(self.editTodo)
        edit_button.setStyleSheet(f"""
            QPushButton {{
                min-height: 30px;
                min-width: 60px;
                padding: 0 15px;
                background-color: {COLORS['success']};
                color: white;
                border: none;
                border-radius: 6px;
                font-weight: bold;
                font-family: 'Microsoft YaHei';
                font-size: 12px;
                text-align: center;
            }}
            QPushButton:hover {{
                background-color: {COLORS['warning']};
            }}
            QPushButton:pressed {{
                background-color: {COLORS['accent']};
            }}
        """)
        
        # 确定按钮
        buttons = QDialogButtonBox(QDialogButtonBox.Ok, Qt.Horizontal)
        buttons.button(QDialogButtonBox.Ok).setText("确定")
        
        ok_button = buttons.button(QDialogButtonBox.Ok)
        ok_button.setMinimumWidth(80)
        
        buttons.accepted.connect(self.accept)
        
        # 添加到布局
        button_layout.addWidget(copy_button)
        button_layout.addWidget(edit_button)
        button_layout.addStretch(1)
        button_layout.addWidget(buttons)
        
        layout.addLayout(button_layout)
        
        # 对话框样式
        self.setStyleSheet(f"""
            QDialog {{
                background-color: {COLORS['background']};
                border-radius: 10px;
            }}
            QTextEdit {{
                border: none;
                border-radius: 6px;
                padding: 15px;
                background-color: {COLORS['card_bg']};
                font-family: 'Microsoft YaHei';
                font-size: 10pt;
                line-height: 1.5;
            }}
        """)
        
        # 保存用于复制
        self.content = content
        self.add_time = datetime_str
        self.reminder_time = reminder_time
        self.is_completed = is_completed
    
    def adjustWindowSize(self, content):
        """动态调整窗口大小以适应内容长度"""
        # 基础窗口大小
        base_width = 450
        base_height = 350
        
        # 根据内容长度调整大小
        if content:
            # 计算内容行数
            lines = content.count('\n') + 1
            # 每行大约需要20像素高度
            content_height = max(100, lines * 20)
            # 设置最小和最大高度
            min_height = max(350, content_height + 150)  # 150是其他元素的高度
            max_height = min(600, min_height)  # 最大高度限制为600
            
            self.resize(base_width, max_height)
        else:
            self.resize(base_width, base_height)
    
    def formatContentForParagraphs(self, content):
        """格式化内容以支持段落显示"""
        if not content:
            return "<p>无内容</p>"
        
        # 将换行符转换为HTML段落
        paragraphs = content.split('\n')
        formatted_paragraphs = []
        
        for paragraph in paragraphs:
            paragraph = paragraph.strip()
            if paragraph:  # 只处理非空段落
                formatted_paragraphs.append(f'<p style="margin: 8px 0; text-align: left;">{paragraph}</p>')
            else:
                # 空行用间距表示
                formatted_paragraphs.append('<p style="margin: 4px 0;">&nbsp;</p>')
        
        # 如果没有段落分隔符，将整个内容作为一个段落
        if len(formatted_paragraphs) == 0:
            return f'<p style="margin: 8px 0; text-align: left;">{content}</p>'
        
        return ''.join(formatted_paragraphs)
    
    def copyContent(self):
        """复制内容到剪贴板"""
        try:
            text = f"状态: {'已完成' if self.is_completed else '待办'}\n"
            text += f"内容: {self.content}\n"
            text += f"添加时间: {self.add_time}\n"
            if self.is_completed and self.reminder_time:
                text += f"提醒时间: {self.reminder_time}\n"
            
            # 安全地设置剪贴板内容
            try:
                clipboard = QApplication.clipboard()
                if clipboard:
                    # 使用更安全的方式设置剪贴板
                    clipboard.clear()  # 先清空剪贴板
                    clipboard.setText(text)
                    print(f"已复制内容到剪贴板: {text[:50]}...")
                else:
                    print("警告: 无法获取剪贴板对象")
                    return
            except Exception as clipboard_error:
                print(f"剪贴板操作失败: {clipboard_error}")
                # 如果剪贴板操作失败，尝试使用系统剪贴板
                try:
                    import pyperclip
                    pyperclip.copy(text)
                    print("使用pyperclip复制成功")
                except ImportError:
                    print("pyperclip未安装，无法使用备用剪贴板")
                    return
                except Exception as pyperclip_error:
                    print(f"pyperclip也失败: {pyperclip_error}")
                    return
            
            # 显示气泡提示
            self.showBubbleTip("内容已复制到剪贴板")
            
        except Exception as e:
            print(f"复制内容时出错: {e}")
            # 如果复制失败，显示错误提示
            QMessageBox.information(self, "复制失败", f"无法复制内容到剪贴板: {str(e)}")
    
    def showBubbleTip(self, message):
        """显示气泡提示"""
        try:
            # 创建气泡提示窗口
            bubble = QWidget()
            bubble.setWindowFlags(Qt.FramelessWindowHint | Qt.ToolTip)
            bubble.setAttribute(Qt.WA_TranslucentBackground)
            bubble.setAttribute(Qt.WA_DeleteOnClose, True)  # 确保窗口关闭时自动删除
            bubble.setStyleSheet("""
                QWidget {
                    background-color: rgba(0, 0, 0, 180);
                    border-radius: 8px;
                    color: white;
                    font-family: 'Microsoft YaHei';
                    font-size: 12px;
                    font-weight: bold;
                }
            """)
            
            # 创建标签显示消息
            label = QLabel(message, bubble)
            label.setAlignment(Qt.AlignCenter)
            label.setStyleSheet("color: white; padding: 8px 12px;")
            
            # 设置布局
            layout = QVBoxLayout(bubble)
            layout.addWidget(label)
            layout.setContentsMargins(0, 0, 0, 0)
            
            # 计算气泡位置（始终保持在代办详情中心区域）
            bubble_size = bubble.sizeHint()
            parent_rect = self.geometry()
            
            # 计算对话框的中心位置
            dialog_center_x = parent_rect.center().x()
            dialog_center_y = parent_rect.center().y()
            
            # 将气泡放置在对话框中心
            x = dialog_center_x - bubble_size.width() // 2
            y = dialog_center_y - bubble_size.height() // 2
            
            # 确保气泡不会超出屏幕边界
            screen = QApplication.primaryScreen()
            if screen:
                screen_rect = screen.availableGeometry()
                if x < screen_rect.left():
                    x = screen_rect.left() + 10
                if x + bubble_size.width() > screen_rect.right():
                    x = screen_rect.right() - bubble_size.width() - 10
                if y < screen_rect.top():
                    y = screen_rect.top() + 10
                if y + bubble_size.height() > screen_rect.bottom():
                    y = screen_rect.bottom() - bubble_size.height() - 10
            
            bubble.move(x, y)
            bubble.show()
            
            # 使用更安全的方式管理气泡的生命周期
            def safe_hide_bubble():
                try:
                    if bubble and not bubble.isHidden():
                        bubble.hide()
                        bubble.close()
                except Exception as e:
                    print(f"隐藏气泡提示时出错: {e}")
            
            # 3秒后自动隐藏
            QTimer.singleShot(3000, safe_hide_bubble)
            
        except Exception as e:
            print(f"显示气泡提示时出错: {e}")
            # 如果气泡提示失败，使用简单的消息框替代
            QMessageBox.information(self, "复制成功", "内容已复制到剪贴板")
    
    def editTodo(self):
        """编辑待办事项"""
        if self.row_index < 0 or not self.parent_window:
            QMessageBox.warning(self, "编辑失败", "无法获取待办事项信息")
            return
        
        # 关闭详情对话框
        self.accept()
        
        # 根据是否在已完成列表中调用相应的编辑方法
        if self.is_completed_list:
            # 编辑已完成事项
            self.parent_window.editCompletedTodo(self.row_index)
        else:
            # 编辑待办事项
            self.parent_window.editTodo(self.row_index)


class CompletionTimeDialog(QDialog):
    """完成时间选择对话框"""
    def __init__(self, parent=None, default_time=None):
        super().__init__(parent)
        self.setWindowTitle("选择提醒时间（取消不添加截止时间）")
        self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
        self.setModal(True)
        self.setFixedSize(400, 300)
        
        if APP_ICON and os.path.exists(APP_ICON):
            self.setWindowIcon(QIcon(APP_ICON))
        
        # 主布局
        layout = QVBoxLayout(self)
        layout.setSpacing(15)
        layout.setContentsMargins(20, 20, 20, 20)
        
        # 时间选择器
        time_label = QLabel("完成时间:")
        time_label.setStyleSheet(f"color: {COLORS['text_primary']}; font-size: 12px; font-weight: bold;")
        layout.addWidget(time_label)
        
        # 设置默认时间（如果未提供，使用当前时间）
        if default_time is None:
            default_time = QDateTime.currentDateTime()
        
        self.datetime_edit = QDateTimeEdit(default_time)
        self.datetime_edit.setDisplayFormat("yyyy-MM-dd hh:mm")
        self.datetime_edit.setCalendarPopup(True)
        self.datetime_edit.setMinimumHeight(35)
        
        # 设置时间选择器样式
        time_edit_style = f"""
            QDateTimeEdit {{
                border-radius: 6px;
                font-size: 12px;
                padding: 8px 12px;
                background-color: {COLORS['background']};
                color: {COLORS['text_primary']};
                border: none;
                font-family: 'Microsoft YaHei';
            }}
            QDateTimeEdit:focus {{
                border-color: {COLORS['primary']};
            }}
            QDateTimeEdit::drop-down {{
                subcontrol-origin: padding;
                subcontrol-position: top right;
                width: 20px;
                border-left: 1px solid {COLORS['border']};
            }}
        """
        self.datetime_edit.setStyleSheet(time_edit_style)
        layout.addWidget(self.datetime_edit)
        
        # 快速时间选择按钮
        quick_time_layout = QHBoxLayout()
        quick_time_layout.setSpacing(5)
        
        # 明天按钮
        tomorrow_btn = QPushButton('明天')
        tomorrow_btn.clicked.connect(lambda: self.setQuickTime(1))
        tomorrow_btn.setFixedSize(50, 30)
        tomorrow_btn.setStyleSheet(self.getQuickButtonStyle())
        quick_time_layout.addWidget(tomorrow_btn)
        
        # 后天按钮
        day_after_btn = QPushButton('后天')
        day_after_btn.clicked.connect(lambda: self.setQuickTime(2))
        day_after_btn.setFixedSize(50, 30)
        day_after_btn.setStyleSheet(self.getQuickButtonStyle())
        quick_time_layout.addWidget(day_after_btn)
        
        # 一周后按钮
        week_btn = QPushButton('一周后')
        week_btn.clicked.connect(lambda: self.setQuickTime(7))
        week_btn.setFixedSize(60, 30)
        week_btn.setStyleSheet(self.getQuickButtonStyle())
        quick_time_layout.addWidget(week_btn)
        
        quick_time_layout.addStretch()
        layout.addLayout(quick_time_layout)
        
        # 按钮区域
        button_layout = QHBoxLayout()
        button_layout.addStretch()
        
        # 确定按钮（从左到右第一个）
        ok_button = QPushButton("确定")
        ok_button.setMinimumWidth(80)
        ok_button.setMinimumHeight(35)
        ok_button.clicked.connect(self.accept)
        ok_button.setStyleSheet(f"""
            QPushButton {{
                background-color: {COLORS['primary']};
                color: white;
                border: none;
                border-radius: 6px;
                font-weight: bold;
                font-family: 'Microsoft YaHei';
                font-size: 12px;
                padding: 8px 16px;
            }}
            QPushButton:hover {{
                background-color: {COLORS['accent']};
            }}
        """)
        button_layout.addWidget(ok_button)
        
        # 取消按钮（从左到右第二个）
        cancel_button = QPushButton("取消")
        cancel_button.setMinimumWidth(80)
        cancel_button.setMinimumHeight(35)
        cancel_button.clicked.connect(self.cancel_without_time)
        cancel_button.setStyleSheet(f"""
            QPushButton {{
                background-color: {COLORS['secondary']};
                color: white;
                border: none;
                border-radius: 6px;
                font-weight: bold;
                font-family: 'Microsoft YaHei';
                font-size: 12px;
                padding: 8px 16px;
            }}
            QPushButton:hover {{
                background-color: {COLORS['accent']};
            }}
        """)
        button_layout.addWidget(cancel_button)
        
        layout.addLayout(button_layout)
        
        # 添加一个标志来区分取消和取消但不设置时间
        self.cancel_without_time_flag = False
    
    def cancel_without_time(self):
        """取消但添加事项，不设置截止时间"""
        self.cancel_without_time_flag = True
        self.accept()  # 使用accept而不是reject，这样调用方会收到Accepted状态
    
    def getQuickButtonStyle(self):
        """获取快速选择按钮样式"""
        return f"""
            QPushButton {{
                background-color: {COLORS['background']};
                color: {COLORS['text_primary']};
                border: 1px solid {COLORS['border']};
                border-radius: 4px;
                font-size: 11px;
                font-family: 'Microsoft YaHei';
                padding: 4px 8px;
            }}
            QPushButton:hover {{
                background-color: {COLORS['primary']};
                color: white;
                border-color: {COLORS['primary']};
            }}
        """
    
    def getSelectedTime(self):
        """获取选择的完成时间"""
        return self.datetime_edit.dateTime().toString("yyyy-MM-dd hh:mm")
    
    def setQuickTime(self, days):
        """设置快速时间"""
        if days == 1:  # 明天
            self.datetime_edit.setDateTime(QDateTime.currentDateTime().addDays(1))
        elif days == 2:  # 后天
            self.datetime_edit.setDateTime(QDateTime.currentDateTime().addDays(2))
        elif days == 7:  # 一周后
            self.datetime_edit.setDateTime(QDateTime.currentDateTime().addDays(7))

# 修改TodoItemWidget类，限制文字显示最多2行
class TodoItemWidget(QFrame):
    """待办项组件"""
    def __init__(self, content, datetime_str, is_completed=False, is_important=False, reminder_time=None, due_date=None, parent=None):
        super().__init__(parent)
        self.setObjectName("TodoItemWidget")
        self.original_content = content
        self.datetime_str = datetime_str
        self.is_completed = is_completed
        self.is_important = is_important
        self.reminder_time = reminder_time
        self.due_date = due_date
        
        # 鼠标悬停状态
        self.is_hovered = False
        # 选中状态
        self.is_selected = False
        
        # 大小策略
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
        
        # 启用鼠标跟踪以接收鼠标移动事件
        self.setMouseTracking(True)
        
        # 布局设置
        self.layout = QVBoxLayout(self)
        self.layout.setContentsMargins(10, 10, 10, 10)
        self.layout.setSpacing(5)
        
        # 内容标签
        self.content_label = QLabel()
        self.content_label.setWordWrap(True)
        self.content_label.setTextFormat(Qt.PlainText)
        self.content_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
        
        # 字体设置
        content_font = QFont("Microsoft YaHei", 10, QFont.Bold)
        self.content_label.setFont(content_font)
        
        # 根据高度设置调整对齐方式
        height_setting = get_todo_height_setting()
        if height_setting == "精简高度（一个汉字高度）":
            # 精简高度模式：内容上对齐
            self.content_label.setAlignment(Qt.AlignLeft | Qt.AlignTop)
        else:
            # 其他模式：内容居中对齐（默认）
            self.content_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
        
        # 颜色设置
        if is_completed:
            # 已完成事项不再区分重要程度，统一使用灰色
            self.content_label.setStyleSheet("color: #CCCCCC;")
        else:
            if is_important:
                self.content_label.setStyleSheet("color: #FF3333;")
            else:
                self.content_label.setStyleSheet("color: #555555;")
        
        # 直接显示内容，不添加重要标记图标
        self.content_label.setText(content)
        
        # 高度限制 - 根据设置调整
        metrics = self.content_label.fontMetrics()
        line_height = metrics.lineSpacing()
        
        # 获取高度设置
        height_setting = get_todo_height_setting()
        
        if height_setting == "自适应高度":
            # 自适应高度：根据内容行数动态调整，不限制最大高度
            self.content_label.setMinimumHeight(line_height)
            self.content_label.setMaximumHeight(16777215)  # 使用Qt的最大值，实际上不限制高度
        elif height_setting == "精简高度（一个汉字高度）":
            # 精简高度：一个汉字高度，限制最大高度以启用截断
            self.content_label.setMinimumHeight(line_height)
            self.content_label.setMaximumHeight(line_height + 5)  # 限制高度以启用截断
        else:
            # 默认高度：固定2行
            max_height = line_height * 2 + 5
            self.content_label.setMinimumHeight(line_height)
            self.content_label.setMaximumHeight(max_height)
        
        # 时间标签 - 根据完成状态调整位置和样式
        if is_completed:
            # 已完成项目：创建水平布局来放置添加时间和完成时间
            time_layout = QHBoxLayout()
            time_layout.setSpacing(5)
            
            # 添加时间标签（左对齐）
            add_time_label = QLabel(f"添加: {datetime_str}")
            add_time_label.setFont(QFont("Microsoft YaHei", 8, QFont.Bold))
            add_time_label.setStyleSheet("color: #666666;")
            add_time_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
            
            # 完成时间标签（右对齐）
            reminder_time_str = self.reminder_time if self.reminder_time else "未知"
            # 只显示月、日、时、分
            if reminder_time_str and reminder_time_str != "未知":
                try:
                    dt = QDateTime.fromString(reminder_time_str, "yyyy-MM-dd HH:mm")
                    if dt.isValid():
                        reminder_time_str = dt.toString("MM-dd HH:mm")
                except:
                    pass
            
            reminder_time_label = QLabel(f"提醒: {reminder_time_str}")
            reminder_time_label.setFont(QFont("Microsoft YaHei", 8, QFont.Bold))
            reminder_time_label.setStyleSheet("color: #4CAF50;")  # 绿色字体
            reminder_time_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
            
            # 将添加时间和提醒时间添加到水平布局
            time_layout.addWidget(add_time_label)
            time_layout.addStretch()  # 中间弹性空间
            time_layout.addWidget(reminder_time_label)
            
            # 添加到主布局
            self.layout.addWidget(self.content_label)
            self.layout.addStretch()  # 添加弹性空间将时间推到底部
            self.layout.addLayout(time_layout)
        else:
            # 未完成项目：时间显示在内容下方，左对齐
            time_layout = QHBoxLayout()
            time_layout.setSpacing(5)
            
            # 添加时间标签
            add_time_label = QLabel(f"添加: {datetime_str}")
            add_time_label.setFont(QFont("Microsoft YaHei", 8, QFont.Bold))
            add_time_label.setStyleSheet("color: #666666;")
            
            # 完成时间标签（如果有设置）
            if due_date:
                # 计算时间状态
                current_time = QDateTime.currentDateTime()
                due_time = QDateTime.fromString(due_date, "yyyy-MM-dd HH:mm")
                
                if due_time.isValid():
                    # 计算时间差（秒）
                    time_diff_seconds = current_time.secsTo(due_time)
                    
                    if time_diff_seconds <= 0:
                        # 已过期，显示超时时间
                        overdue_seconds = abs(time_diff_seconds)
                        hours = overdue_seconds // 3600
                        minutes = (overdue_seconds % 3600) // 60
                        due_text = f"超时：{hours}小时{minutes}分"
                        due_color = "#FF3333"  # 红色
                    elif time_diff_seconds <= 60:
                        # 1分钟内到期
                        due_text = "今天到期"
                        due_color = "#FF6B35"  # 橙色
                    elif time_diff_seconds <= 3600:
                        # 1小时内到期
                        minutes_left = time_diff_seconds // 60
                        due_text = f"还有{minutes_left}分钟到期"
                        due_color = "#FF6B35"  # 橙色
                    elif time_diff_seconds <= 86400:
                        # 判断是否是今天还是明天
                        current_date = current_time.date()
                        due_date = due_time.date()
                        
                        if current_date == due_date:
                            # 今天到期
                            hours_left = time_diff_seconds // 3600
                            due_text = f"今天 {due_time.toString('HH:mm')} 到期"
                            due_color = "#FFA500"  # 橙色
                        elif due_date == current_date.addDays(1):
                            # 明天到期
                            due_text = f"明天 {due_time.toString('HH:mm')} 到期"
                            due_color = "#FFA500"  # 橙色
                        else:
                            # 其他情况，显示天数
                            days_left = time_diff_seconds // 86400
                            due_text = f"{days_left}天后到期"
                            due_color = "#FFA500"  # 橙色
                    elif time_diff_seconds <= 259200:
                        # 3天内到期
                        days_left = time_diff_seconds // 86400
                        due_text = f"{days_left}天后到期"
                        due_color = "#FFA500"  # 橙色
                    else:
                        # 还有充足时间
                        days_left = time_diff_seconds // 86400
                        due_text = f"{days_left}天后到期"
                        due_color = "#4CAF50"  # 绿色
                    
                    # 根据是否超时决定显示文本
                    if time_diff_seconds <= 0:
                        # 超时时只显示"超时"，不显示"截止"
                        due_time_label = QLabel(due_text)
                    else:
                        # 未超时时显示"截止"
                        due_time_label = QLabel(f"截止: {due_text}")
                    due_time_label.setFont(QFont("Microsoft YaHei", 8, QFont.Bold))
                    due_time_label.setStyleSheet(f"color: {due_color};")
                    
                    time_layout.addWidget(add_time_label)
                    time_layout.addWidget(due_time_label)
                else:
                    time_layout.addWidget(add_time_label)
            else:
                time_layout.addWidget(add_time_label)
            
            time_layout.addStretch()  # 添加弹性空间
            
            # 内容在上，时间在下
            self.layout.addWidget(self.content_label)
            self.layout.addLayout(time_layout)
        
        # 背景色设置
        if not is_completed:
            bg_color = "#e6f7ff"
        else:
            bg_color = "#f0f8ff"
        
        self.setAutoFillBackground(True)
        p = self.palette()
        p.setColor(self.backgroundRole(), QColor(bg_color))
        self.setPalette(p)
        
        # 应用初始样式
        self.updateStyle()
        
        # 文本截断
        self._truncateTextWithEllipsis()
        self.layout.activate()
    
    def updateStyle(self):
        """更新样式，包括悬停效果"""
        # 基础背景色
        if not self.is_completed:
            if self.is_important:
                bg_color = "#fff2e6"  # 重要事项使用橙色背景
            else:
                bg_color = "#e6f7ff"  # 普通事项使用蓝色背景
        else:
            # 已完成的事项不再区分重要程度，统一使用浅蓝色背景
            bg_color = "#f0f8ff"  # 已完成事项使用浅蓝色背景
        
        # 悬停和选中时使用不同的背景色
        if self.is_selected:
            # 选中状态：较深的蓝色背景
            bg_color = "rgba(67, 97, 238, 0.25)"
        elif self.is_hovered:
            # 悬停状态：较浅的蓝色背景
            bg_color = "rgba(67, 97, 238, 0.1)"
        
        # 样式设置
        self.setStyleSheet(f"""
            QFrame#TodoItemWidget {{
                border: none;
                border-radius: 4px;
                background-color: {bg_color};
                width: 100%;
                transition: background-color 0.2s ease;
            }}
            QFrame#TodoItemWidget:hover {{
                background-color: {bg_color};
            }}
            QLabel {{
                background-color: transparent;
                border: none;
                width: 100%;
            }}
        """)
        
        # 更新调色板
        p = self.palette()
        p.setColor(self.backgroundRole(), QColor(bg_color))
        self.setPalette(p)
    
    def setSelected(self, selected):
        """设置选中状态"""
        if self.is_selected != selected:
            self.is_selected = selected
            self.updateStyle()
    
    def enterEvent(self, event):
        """鼠标进入事件"""
        self.is_hovered = True
        self.updateStyle()
        # 改变鼠标指针样式
        self.setCursor(Qt.PointingHandCursor)
        super().enterEvent(event)
    
    def leaveEvent(self, event):
        """鼠标离开事件"""
        self.is_hovered = False
        self.updateStyle()
        # 恢复默认鼠标指针样式
        self.setCursor(Qt.ArrowCursor)
        super().leaveEvent(event)
    
    def mousePressEvent(self, event):
        """鼠标按下事件"""
        if event.button() == Qt.LeftButton:
            # 按下状态：最深的蓝色背景
            press_color = "rgba(67, 97, 238, 0.3)"
                
            self.setStyleSheet(f"""
                QFrame#TodoItemWidget {{
                    border: none;
                    border-radius: 4px;
                    background-color: {press_color};
                    width: 100%;
                }}
                QLabel {{
                    background-color: transparent;
                    border: none;
                    width: 100%;
                }}
            """)
        super().mousePressEvent(event)
    
    def mouseReleaseEvent(self, event):
        """鼠标释放事件"""
        if event.button() == Qt.LeftButton:
            # 恢复悬停状态样式
            self.updateStyle()
        super().mouseReleaseEvent(event)
    
    def _truncateTextWithEllipsis(self):
        """截断文本并添加省略号，根据高度设置决定是否截断"""
        # 获取原始内容
        text = self.original_content
        
        # 获取高度设置
        height_setting = get_todo_height_setting()
        
        # 获取字体度量，在所有代码路径中都需要使用
        metrics = self.content_label.fontMetrics()
        
        # 如果是自适应高度模式，不截断文本
        if height_setting == "自适应高度":
            self.content_label.setText(text)
            return
        
        # 如果是精简高度模式，启用截断逻辑
        if height_setting == "精简高度（一个汉字高度）":
            # 精简高度：计算能在一个汉字高度内显示的最大文本长度
            width = self.width() if self.width() > 0 else 150
            available_width = width - 20
            
            # 计算一个汉字高度能显示的最大文本长度
            line_height = metrics.lineSpacing()
            max_height = line_height + 5
            
            # 逐字符尝试，找到能在一个汉字高度内显示的最大文本长度
            start = 0
            end = len(text)
            best_text = text
            
            while start <= end:
                mid = (start + end) // 2
                test_text = text[:mid] + "..."
                
                test_rect = metrics.boundingRect(
                    0, 0, available_width, 1000,
                    Qt.TextWordWrap, test_text
                )
                
                if test_rect.height() <= max_height:
                    # 这个长度可以，尝试更长的文本
                    best_text = test_text
                    start = mid + 1
                else:
                    # 这个长度太长，尝试更短的文本
                    end = mid - 1
            
            self.content_label.setText(best_text)
            return
        
        # 根据当前宽度计算能显示的文字量
        width = self.width() if self.width() > 0 else 150  # 减小默认宽度以适应窄窗口
        available_width = width - 20  # 减小内边距以适应窄窗口
        
        # 如果内容能在2行内完全显示，则直接显示完整内容
        text_rect = metrics.boundingRect(
            0, 0, available_width, 1000,  # 高度给足够大
            Qt.TextWordWrap, text
        )
        
        # 计算2行文本的高度
        line_height = metrics.lineSpacing()
        max_height = line_height * 2
        
        if text_rect.height() <= max_height:
            # 内容能在2行内完全显示
            self.content_label.setText(text)
            return
        
        # 内容超过2行，需要截断
        # 逐字符尝试，找到能在2行内显示的最大文本长度
        start = 0
        end = len(text)
        best_text = text
        
        while start <= end:
            mid = (start + end) // 2
            test_text = text[:mid] + "..."
            
            test_rect = metrics.boundingRect(
                0, 0, available_width, 1000,
                Qt.TextWordWrap, test_text
            )
            
            if test_rect.height() <= max_height:
                # 这个长度可以，尝试更长的文本
                best_text = test_text
                start = mid + 1
            else:
                # 这个长度太长，尝试更短的文本
                end = mid - 1
        
        self.content_label.setText(best_text)
    
    # 重写sizeHint方法，以确保小部件可以适应列表宽度
    def sizeHint(self):
        """确保小部件在宽度计算上更加准确，不会超出边界"""
        # 获取父组件宽度 - 通常是列表视图的viewport
        width = self.parent().width() if self.parent() else 250
        # 减去足够的边距，确保不会超出边界（左右外边距20+内边距10*2）
        width = max(200, width - 40)
        
        # 获取高度设置
        height_setting = get_todo_height_setting()
        
        # 根据高度设置决定内容高度
        metrics = self.content_label.fontMetrics()
        line_height = metrics.lineSpacing()
        
        if height_setting == "自适应高度":
            # 自适应高度：根据原始内容计算实际需要的行数
            content_text = self.original_content  # 使用原始内容而不是可能被截断的文本
            text_width = width - 20  # 减去左右边距
            
            # 使用QFontMetrics的boundingRect来计算实际需要的行数
            text_rect = metrics.boundingRect(
                0, 0, text_width, 1000,  # 高度给足够大
                Qt.TextWordWrap, content_text
            )
            
            # 计算实际行数，至少1行，最多5行
            actual_lines = max(1, min(text_rect.height() // line_height, 5))
            content_height = line_height * actual_lines + 10
        elif height_setting == "精简高度（一个汉字高度）":
            # 精简高度：一个汉字高度
            content_height = line_height + 10
        else:
            # 默认高度：固定2行
            content_height = line_height * 2 + 10
        
        # 根据完成状态计算时间标签高度
        if self.is_completed:
            # 已完成项目：添加时间和完成时间在同一行
            time_height = line_height + 10
        else:
            # 未完成项目：时间标签在内容下方
            if hasattr(self, 'datetime_label'):
                time_height = self.datetime_label.sizeHint().height()
            else:
                time_height = line_height + 10
        
        total_height = content_height + time_height + 25  # 加上布局间距和边距
        
        return QSize(width, total_height)
    
    # 重写resizeEvent，以便在大小变化时调整文本显示
    def resizeEvent(self, event):
        """当项目大小改变时，重新调整内容显示"""
        super().resizeEvent(event)
        # 当大小改变时，需要重新计算文本截断
        self._truncateTextWithEllipsis()
        # 激活布局以确保更新
        self.layout.activate()
    
    def updateTimeDisplay(self):
        """更新时间显示，用于定时刷新"""
        if self.is_completed:
            # 已完成事项不需要更新时间显示
            return
        
        # 只更新未完成事项的截止时间显示
        if self.due_date:
            # 找到时间布局中的截止时间标签并更新
            for i in range(self.layout.count()):
                item = self.layout.itemAt(i)
                if item and item.layout():
                    layout = item.layout()
                    for j in range(layout.count()):
                        widget = layout.itemAt(j).widget()
                        if isinstance(widget, QLabel) and (widget.text().startswith("截止:") or widget.text().startswith("超时：")):
                            # 重新计算时间状态
                            current_time = QDateTime.currentDateTime()
                            due_time = QDateTime.fromString(self.due_date, "yyyy-MM-dd HH:mm")
                            
                            if due_time.isValid():
                                # 计算时间差（秒）
                                time_diff_seconds = current_time.secsTo(due_time)
                                
                                if time_diff_seconds <= 0:
                                    # 已过期，显示超时时间
                                    overdue_seconds = abs(time_diff_seconds)
                                    hours = overdue_seconds // 3600
                                    minutes = (overdue_seconds % 3600) // 60
                                    due_text = f"超时：{hours}小时{minutes}分"
                                    due_color = "#FF3333"  # 红色
                                elif time_diff_seconds <= 60:
                                    # 1分钟内到期
                                    due_text = "今天到期"
                                    due_color = "#FF6B35"  # 橙色
                                elif time_diff_seconds <= 3600:
                                    # 1小时内到期
                                    minutes_left = time_diff_seconds // 60
                                    due_text = f"还有{minutes_left}分钟到期"
                                    due_color = "#FF6B35"  # 橙色
                                elif time_diff_seconds <= 86400:
                                    # 判断是否是今天还是明天
                                    current_date = current_time.date()
                                    due_date = due_time.date()
                                    
                                    if current_date == due_date:
                                        # 今天到期
                                        hours_left = time_diff_seconds // 3600
                                        due_text = f"今天 {due_time.toString('HH:mm')} 到期"
                                        due_color = "#FFA500"  # 橙色
                                    elif due_date == current_date.addDays(1):
                                        # 明天到期
                                        due_text = f"明天 {due_time.toString('HH:mm')} 到期"
                                        due_color = "#FFA500"  # 橙色
                                    else:
                                        # 其他情况，显示天数
                                        days_left = time_diff_seconds // 86400
                                        due_text = f"{days_left}天后到期"
                                        due_color = "#FFA500"  # 橙色
                                elif time_diff_seconds <= 259200:
                                    # 3天内到期
                                    days_left = time_diff_seconds // 86400
                                    due_text = f"{days_left}天后到期"
                                    due_color = "#FFA500"  # 橙色
                                else:
                                    # 还有充足时间
                                    days_left = time_diff_seconds // 86400
                                    due_text = f"{days_left}天后到期"
                                    due_color = "#4CAF50"  # 绿色
                                
                                # 更新标签文本和颜色
                                if time_diff_seconds <= 0:
                                    # 超时时只显示"超时"，不显示"截止"
                                    widget.setText(due_text)
                                else:
                                    # 未超时时显示"截止"
                                    widget.setText(f"截止: {due_text}")
                                widget.setStyleSheet(f"color: {due_color};")
                            return  # 找到并更新后直接返回

class TodoEditDialog(QDialog):
    """自定义的待办事项编辑对话框"""
    def __init__(self, parent=None, content="", dt=None, reminder_time=None, due_date=None):
        super().__init__(parent)
        self.setWindowTitle(f"编辑待办事项 {APP_VERSION}")  # 添加版本号
        self.resize(450, 400)  # 调整对话框大小以适应所有元素
        
        # 设置窗口图标
        icon_path = APP_ICON
        if icon_path and os.path.exists(icon_path):
            self.setWindowIcon(QIcon(icon_path))
        
        # 移除窗口右上角的问号按钮
        self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
        
        # 如果没有提供日期时间，使用当前时间
        if dt is None:
            dt = QDateTime.currentDateTime()
        
        # 处理提醒时间：优先使用reminder_time，如果没有则使用due_date，如果都没有则使用空时间
        if reminder_time:
            try:
                reminder_dt = QDateTime.fromString(reminder_time, "yyyy-MM-dd HH:mm")
            except:
                reminder_dt = QDateTime()
        elif due_date:
            try:
                reminder_dt = QDateTime.fromString(due_date, "yyyy-MM-dd HH:mm")
            except:
                reminder_dt = QDateTime()
        else:
            reminder_dt = QDateTime()
        
        # 创建表单布局
        layout = QFormLayout(self)
        layout.setSpacing(15)
        layout.setContentsMargins(20, 20, 20, 20)
        
        # 内容多行输入框 - 使用QTextEdit替代QLineEdit
        self.content_edit = QTextEdit()
        self.content_edit.setText(content)
        self.content_edit.setMinimumHeight(80)  # 设置合适的高度
        self.content_edit.setMaximumHeight(120)  # 限制最大高度
        self.content_edit.setAcceptRichText(False)  # 不接受富文本，保持纯文本编辑
        self.content_edit.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)  # 需要时显示滚动条
        layout.addRow("内容:", self.content_edit)
        
        # 日期时间选择器
        self.datetime_edit = QDateTimeEdit(dt)
        self.datetime_edit.setDisplayFormat("yyyy-MM-dd HH:mm")
        self.datetime_edit.setCalendarPopup(True)
        self.datetime_edit.setMinimumHeight(35)
        layout.addRow("添加时间:", self.datetime_edit)
        
        # 提醒时间选择区域
        reminder_layout = QHBoxLayout()
        
        # 提醒时间选择按钮
        self.reminder_checkbox = QCheckBox("设置提醒时间")
        self.reminder_checkbox.setChecked(reminder_dt.isValid())
        self.reminder_checkbox.stateChanged.connect(self.toggleReminderTime)
        reminder_layout.addWidget(self.reminder_checkbox)
        
        # 提醒时间选择器
        self.reminder_time_edit = QDateTimeEdit(reminder_dt if reminder_dt.isValid() else QDateTime.currentDateTime())
        self.reminder_time_edit.setDisplayFormat("yyyy-MM-dd HH:mm")
        self.reminder_time_edit.setCalendarPopup(True)
        self.reminder_time_edit.setMinimumHeight(35)
        self.reminder_time_edit.setEnabled(self.reminder_checkbox.isChecked())
        reminder_layout.addWidget(self.reminder_time_edit)
        
        layout.addRow("提醒设置:", reminder_layout)
        
        # 按钮盒子 - 使用中文按钮文本
        buttons = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel,
            Qt.Horizontal, self)
        buttons.button(QDialogButtonBox.Ok).setText("确定")  # 将OK改为确定
        buttons.button(QDialogButtonBox.Cancel).setText("取消")  # 将Cancel改为取消
        
        # 设置按钮宽度
        ok_button = buttons.button(QDialogButtonBox.Ok)
        cancel_button = buttons.button(QDialogButtonBox.Cancel)
        ok_button.setMinimumWidth(120)
        cancel_button.setMinimumWidth(120)
        
        buttons.accepted.connect(self.accept)
        buttons.rejected.connect(self.reject)
        layout.addRow(buttons)
        
        # 设置对话框样式
        self.setStyleSheet(f"""
            QDialog {{
                background-color: {COLORS['background']};
                border-radius: 10px;
            }}
            QTextEdit {{  /* 更新样式以适应QTextEdit */
                border: 1px solid {COLORS['border']};
                border-radius: 6px;
                padding: 8px;
                font-size: 14px;
                font-family: 'Microsoft YaHei';
                background-color: {COLORS['card_bg']};
                selection-background-color: {COLORS['primary']};
                min-height: 80px;
                max-height: 120px;
            }}
            QTextEdit:focus {{
                border: 2px solid {COLORS['primary']};
            }}
            QDateTimeEdit {{
                height: 35px;
                border: 1px solid {COLORS['border']};
                border-radius: 6px;
                padding: 2px 10px;
                font-size: 14px;
                font-family: 'Microsoft YaHei';
                font-weight: bold;
                background-color: {COLORS['card_bg']};
                selection-background-color: {COLORS['primary']};
            }}
            QDateTimeEdit:focus {{
                border: 2px solid {COLORS['primary']};
            }}
            QDateTimeEdit:disabled {{
                background-color: {COLORS['background']};
                color: {COLORS['text_secondary']};
                border: 1px solid {COLORS['border']};
            }}
            QPushButton {{
                min-height: 35px;
                min-width: 120px;
                padding: 8px 15px;
                background-color: {COLORS['primary']};
                color: white;
                border: none;
                border-radius: 6px;
                font-weight: bold;
                font-family: 'Microsoft YaHei';
                font-size: 14px;
            }}
            QPushButton:hover {{
                background-color: {COLORS['accent']};
            }}
            QPushButton:pressed {{
                background-color: {COLORS['secondary']};
            }}
            QLabel {{
                font-size: 14px;
                color: {COLORS['text_primary']};
                font-family: 'Microsoft YaHei';
                font-weight: bold;
            }}
            QCheckBox {{
                font-size: 14px;
                color: {COLORS['text_primary']};
                font-family: 'Microsoft YaHei';
                font-weight: bold;
                spacing: 8px;
            }}
            QCheckBox::indicator {{
                width: 18px;
                height: 18px;
                border: 2px solid {COLORS['border']};
                border-radius: 3px;
                background-color: {COLORS['card_bg']};
            }}
            QCheckBox::indicator:checked {{
                background-color: {COLORS['primary']};
                border-color: {COLORS['primary']};
            }}
            QCheckBox::indicator:checked:after {{
                content: "✓";
                color: white;
                font-weight: bold;
            }}
        """)
        
        # 设置阴影效果
        shadow = QGraphicsDropShadowEffect(self)
        shadow.setBlurRadius(20)
        shadow.setColor(QColor(0, 0, 0, 80))
        shadow.setOffset(0, 5)
        self.setGraphicsEffect(shadow)
        
        # 将对话框居中显示在父窗口
        if parent:
            self.move(parent.frameGeometry().center() - self.rect().center())
        
        # 在编辑对话框后面，主界面中加入"搜索"入口的方法（供外部调用）
        # 注意：实际调用位置会在 MainWindow 的 UI 构建完成后绑定按钮
    
    def toggleReminderTime(self, state):
        """切换提醒时间选择器的启用状态"""
        self.reminder_time_edit.setEnabled(state == Qt.Checked)
        if state == Qt.Checked and not self.reminder_time_edit.dateTime().isValid():
            # 如果启用但时间无效，设置为当前时间
            self.reminder_time_edit.setDateTime(QDateTime.currentDateTime())
    
    def getInputs(self):
        """获取用户输入的内容、日期时间和提醒时间"""
        if self.reminder_checkbox.isChecked():
            reminder_time = self.reminder_time_edit.dateTime()
            reminder_time_str = reminder_time.toString("yyyy-MM-dd HH:mm") if reminder_time.isValid() else None
        else:
            reminder_time_str = None
        return self.content_edit.toPlainText(), self.datetime_edit.dateTime(), reminder_time_str

# ==================== 插件相关类 ====================

class APIAnalysisThread(QThread):
    """API分析线程"""
    result_ready = pyqtSignal(str)
    error_occurred = pyqtSignal(str)
    
    def __init__(self, api_key, base_url, model, content, provider="DeepSeek"):
        super().__init__()
        self.api_key = api_key
        self.base_url = base_url
        self.model = model
        self.content = content
        self.provider = provider
        
    def run(self):
        try:
            # 尝试导入openai
            try:
                from openai import OpenAI
            except ImportError:
                self.error_occurred.emit("请先安装OpenAI SDK: pip install openai")
                return
                
            # 创建客户端
            # 确保base_url以斜杠结尾，这样OpenAI SDK会自动添加正确的端点路径
            base_url = self.base_url.rstrip('/') + '/'
            print(f"🔧 API配置信息:")
            print(f"   提供商: {self.provider}")
            print(f"   Base URL: {base_url}")
            print(f"   模型: {self.model}")
            print(f"   API Key: {self.api_key[:8]}...")
            
            client = OpenAI(api_key=self.api_key, base_url=base_url)
            
            # 构建待办事项分析提示词
            system_prompt = """你是一个专业的待办事项分析助手。请分析用户提供的内容，将其转换为结构化的待办事项列表。

请按照以下格式输出：

### 🎯 主要任务
· 任务1（优先级：高/中/低）
· 任务2（优先级：高/中/低）

请用中文回答，格式要清晰易读，任务描述要具体明确。"""
            
            user_prompt = f"请将以下内容分析为待办事项：\n\n{self.content}"
            
            # 调用API
            # 根据提供商设置不同的参数
            api_params = {
                "model": self.model,
                "messages": [
                    {"role": "system", "content": system_prompt},
                    {"role": "user", "content": user_prompt},
                ],
                "stream": False,
                "max_tokens": 1500,
                "temperature": 0.7
            }
            
            # 如果是腾讯混元，添加特殊参数
            if self.provider == "腾讯混元":
                api_params["extra_body"] = {
                    "enable_enhancement": True
                }
            # 如果是通义大模型，添加特殊参数
            elif self.provider == "通义大模型":
                api_params["extra_body"] = {
                    "enable_thinking": False
                }
            
            print(f"🚀 开始API调用...")
            print(f"   请求参数: {api_params}")
            
            response = client.chat.completions.create(**api_params)
            
            print(f"✅ API调用成功!")
            print(f"   响应状态: {response}")
            
            result = response.choices[0].message.content
            self.result_ready.emit(result)
            
        except Exception as e:
            import traceback
            error_details = f"API调用失败: {str(e)}"
            print(f"❌ {error_details}")
            print(f"   错误类型: {type(e).__name__}")
            print(f"   错误详情: {str(e)}")
            print(f"   完整错误信息:")
            traceback.print_exc()
            self.error_occurred.emit(error_details)

class APITestThread(QThread):
    """API测试线程"""
    result_ready = pyqtSignal(str)
    error_occurred = pyqtSignal(str)

class UpdateCheckThread(QThread):
    """更新检查线程"""
    update_available = pyqtSignal(str, str, str)  # version, changelog, download_url
    no_update = pyqtSignal()
    error_occurred = pyqtSignal(str)
    
    def __init__(self):
        super().__init__()
    
    def run(self):
        """执行更新检查"""
        max_retries = 3
        retry_delay = 1  # 秒
        
        for attempt in range(max_retries):
            try:
                print(f"正在检查更新... (尝试 {attempt + 1}/{max_retries})")
                
                # 从API获取版本信息
                req = urllib.request.Request(UPDATE_URL, headers={
                    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
                    'Accept': 'application/json',
                    'Connection': 'keep-alive'
                })
                
                # 增加超时时间并添加重试机制
                with urllib.request.urlopen(req, timeout=15) as response:
                    # 读取原始数据
                    raw_data = response.read()
                    # 尝试不同的编码方式来处理BOM
                    try:
                        # 先尝试utf-8-sig，它会自动处理BOM
                        content = raw_data.decode('utf-8-sig')
                    except UnicodeDecodeError:
                        # 如果失败，尝试普通utf-8
                        content = raw_data.decode('utf-8')
                    
                    # 移除可能的BOM字符
                    if content.startswith('\ufeff'):
                        content = content[1:]
                    
                    data = json.loads(content)

                remote_version = data.get('version', '')
                changelog = data.get('changelog', '')
                download_url = data.get('download_url', '')

                print(f"远程版本: {remote_version}")
                print(f"本地版本: {APP_VERSION}")

                # 比较版本号
                if self._compare_versions(remote_version, APP_VERSION) > 0:
                    # 有新版本
                    self.update_available.emit(remote_version, changelog, download_url)
                else:
                    # 已是最新版本
                    self.no_update.emit()
                
                # 成功则退出重试循环
                return

            except urllib.error.URLError as e:
                error_msg = str(e)
                print(f"网络错误 (尝试 {attempt + 1}/{max_retries}): {error_msg}")
                
                if attempt < max_retries - 1:
                    print(f"等待 {retry_delay} 秒后重试...")
                    import time
                    time.sleep(retry_delay)
                    retry_delay *= 2  # 指数退避
                else:
                    # 最后一次尝试失败，提供详细的错误信息
                    if "10053" in error_msg:
                        detailed_error = "网络连接被中断，可能的原因：\n\n" \
                                       "1. 防火墙或杀毒软件阻止了连接\n" \
                                       "2. 网络不稳定或连接超时\n" \
                                       "3. 服务器暂时不可用\n\n" \
                                       "建议：\n" \
                                       "• 检查网络连接\n" \
                                       "• 暂时关闭防火墙/杀毒软件\n" \
                                       "• 稍后再试"
                    elif "timeout" in error_msg.lower():
                        detailed_error = "连接超时，可能的原因：\n\n" \
                                       "1. 网络速度较慢\n" \
                                       "2. 服务器响应慢\n" \
                                       "3. 网络不稳定\n\n" \
                                       "建议：\n" \
                                       "• 检查网络连接\n" \
                                       "• 稍后再试"
                    else:
                        detailed_error = f"网络错误：{error_msg}\n\n" \
                                       "请检查：\n" \
                                       "• 网络连接是否正常\n" \
                                       "• 防火墙设置\n" \
                                       "• 代理设置"
                    
                    self.error_occurred.emit(detailed_error)
                    
            except json.JSONDecodeError as e:
                self.error_occurred.emit(f"数据解析错误：服务器返回的数据格式不正确\n\n错误详情：{str(e)}\n\n请稍后再试或联系技术支持。")
                return
                
            except Exception as e:
                self.error_occurred.emit(f"检查更新时发生未知错误：\n\n{str(e)}\n\n请稍后再试。")
                return
    
    def _compare_versions(self, version1, version2):
        """比较版本号，返回1表示version1大于version2，-1表示小于，0表示相等"""
        try:
            v1_parts = [int(x) for x in version1.replace('V', '').split('.')]
            v2_parts = [int(x) for x in version2.replace('V', '').split('.')]

            # 确保两个版本号长度一致
            max_len = max(len(v1_parts), len(v2_parts))
            v1_parts.extend([0] * (max_len - len(v1_parts)))
            v2_parts.extend([0] * (max_len - len(v2_parts)))

            for i in range(len(v1_parts)):
                if v1_parts[i] > v2_parts[i]:
                    return 1
                elif v1_parts[i] < v2_parts[i]:
                    return -1
            return 0
        except:
            return 0

class UpdateDownloadThread(QThread):
    """更新下载线程"""
    progress_updated = pyqtSignal(str)  # 进度更新信号
    download_finished = pyqtSignal(str, str)  # 下载完成信号 (success, file_path)
    download_error = pyqtSignal(str)  # 下载错误信号
    
    def __init__(self, download_url):
        super().__init__()
        self.download_url = download_url
    
    def run(self):
        """执行下载"""
        try:
            print(f"开始下载新版本: {self.download_url}")
            self.progress_updated.emit("正在下载更新...")

            # 下载文件
            req = urllib.request.Request(self.download_url, headers={
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
                'Accept': '*/*',
                'Connection': 'keep-alive'
            })
            with urllib.request.urlopen(req, timeout=60) as response:
                total_size = response.headers.get('content-length')
                if total_size:
                    total_size = int(total_size)

                # 获取安全的下载目录（避免中文路径问题）
                try:
                    # 尝试使用Windows的临时目录（通常是英文路径）
                    download_dir = tempfile.gettempdir()
                    print(f"使用临时目录: {download_dir}")
                except:
                    try:
                        # 如果失败，尝试使用当前工作目录
                        download_dir = os.getcwd()
                        print(f"使用当前目录: {download_dir}")
                    except:
                        # 最后尝试用户目录下的Downloads文件夹
                        user_home = os.path.expanduser("~")
                        download_dir = os.path.join(user_home, "Downloads")
                        if not os.path.exists(download_dir):
                            download_dir = user_home
                        print(f"使用用户目录: {download_dir}")
                
                # 使用简单的文件名避免编码问题
                temp_filename = "main.exe.tmp"
                final_filename = "main.exe"
                
                # 确保路径是ASCII编码安全的
                try:
                    temp_path = os.path.join(download_dir, temp_filename)
                    final_path = os.path.join(download_dir, final_filename)
                    # 测试路径是否可以正确编码
                    temp_path.encode('ascii')
                    final_path.encode('ascii')
                except UnicodeEncodeError:
                    # 如果路径包含非ASCII字符，使用更安全的方法
                    download_dir = "C:\\temp" if os.name == 'nt' else "/tmp"
                    if not os.path.exists(download_dir):
                        os.makedirs(download_dir, exist_ok=True)
                    temp_path = os.path.join(download_dir, temp_filename)
                    final_path = os.path.join(download_dir, final_filename)
                
                print(f"下载路径: {final_path}")
                print(f"临时文件: {temp_path}")

                downloaded = 0
                last_progress = -1  # 记录上次的进度百分比
                with open(temp_path, 'wb') as f:
                    while True:
                        chunk = response.read(8192)
                        if not chunk:
                            break
                        f.write(chunk)
                        downloaded += len(chunk)

                        # 更新进度（如果有总大小），但限制更新频率
                        if total_size:
                            progress = int((downloaded / total_size) * 100)
                            # 只在进度变化超过5%时才更新，避免UI阻塞
                            if progress != last_progress and progress % 5 == 0:
                                self.progress_updated.emit(f"正在下载更新... {progress}%")
                                last_progress = progress
                                # 让出CPU时间，避免阻塞UI
                                import time
                                time.sleep(0.01)
                        else:
                            # 如果没有总大小，显示已下载的字节数
                            if downloaded % (1024 * 1024) == 0:  # 每1MB更新一次
                                mb_downloaded = downloaded // (1024 * 1024)
                                self.progress_updated.emit(f"正在下载更新... {mb_downloaded}MB")
                                import time
                                time.sleep(0.01)

                # 重命名临时文件为正式文件
                if os.path.exists(final_path):
                    os.remove(final_path)
                os.rename(temp_path, final_path)

                # 下载完成
                self.download_finished.emit("success", final_path)

        except Exception as e:
            print(f"下载更新失败: {e}")
            self.download_error.emit(f"下载更新失败：\n{str(e)}")


class APITestThread(QThread):
    """API测试线程"""
    result_ready = pyqtSignal(str)
    error_occurred = pyqtSignal(str)
    
    def __init__(self, api_key, base_url, model, provider="DeepSeek"):
        super().__init__()
        self.api_key = api_key
        self.base_url = base_url
        self.model = model
        self.provider = provider
        
    def run(self):
        try:
            # 尝试导入openai
            try:
                from openai import OpenAI
            except ImportError:
                self.error_occurred.emit("请先安装OpenAI SDK: pip install openai")
                return
                
            # 创建客户端
            base_url = self.base_url.rstrip('/') + '/'
            print(f"🧪 API测试信息:")
            print(f"   提供商: {self.provider}")
            print(f"   Base URL: {base_url}")
            print(f"   模型: {self.model}")
            
            client = OpenAI(
                api_key=self.api_key,
                base_url=base_url
            )
            
            # 发送测试请求
            response = client.chat.completions.create(
                model=self.model,
                messages=[
                    {"role": "user", "content": "请回复'测试成功'"}
                ],
                max_tokens=50,
                temperature=0.1
            )
            
            # 获取回复
            reply = response.choices[0].message.content
            print(f"✅ API测试成功，回复: {reply}")
            
            # 发送结果
            result = f"API测试成功！\n\n"
            result += f"提供商: {self.provider}\n"
            result += f"Base URL: {base_url}\n"
            result += f"模型: {self.model}\n"
            result += f"回复内容: {reply}\n"
            
            self.result_ready.emit(result)
            
        except Exception as e:
            error_details = f"API测试失败: {str(e)}"
            print(f"❌ API测试失败: {str(e)}")
            print(f"   错误详情: {str(e)}")
            print(f"   完整错误信息:")
            import traceback
            traceback.print_exc()
            self.error_occurred.emit(error_details)

class PluginConfigDialog(QDialog):
    """插件配置对话框"""
    def __init__(self, parent=None, config=None):
        super().__init__(parent)
        self.config = config or {}
        
        # 确保配置结构完整
        if 'providers' not in self.config:
            self.config['providers'] = {}
        if 'current_provider' not in self.config:
            self.config['current_provider'] = self.config.get('api_provider', 'DeepSeek')
        
        self.setup_ui()
        self.load_config_to_ui()
        
        # 窗口居中显示在父窗口中心
        if parent:
            self.center_on_parent()
        
    def setup_ui(self):
        self.setWindowTitle("插件配置")
        self.setMinimumSize(550, 650)  # 设置合理的窗口大小
        self.resize(550, 650)
        # 设置窗口标志，确保下拉列表能正常显示
        self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
        # 确保窗口可以调整大小，为下拉列表提供足够空间
        self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
        
        # 设置窗口图标
        icon_path = get_icon_path()
        if icon_path and os.path.exists(icon_path):
            self.setWindowIcon(QIcon(icon_path))
        
        # 设置中文右键菜单 - 完全禁用默认菜单
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.show_config_context_menu)
        
        # 禁用所有子控件的默认右键菜单
        self.setAttribute(Qt.WA_NoMousePropagation, False)
        
        layout = QVBoxLayout()
        layout.setSpacing(15)
        layout.setContentsMargins(20, 20, 20, 20)
        
        # API配置组
        api_group = QGroupBox("API配置")
        api_layout = QVBoxLayout(api_group)
        api_layout.setSpacing(12)  # 增加间距
        api_layout.setContentsMargins(15, 15, 15, 20)  # 增加底部边距
        
        # API Key输入
        api_key_label = QLabel("API Key:")
        api_key_label.setFont(QFont("微软雅黑", 10, QFont.Bold))
        api_layout.addWidget(api_key_label)
        
        self.api_key_input = QLineEdit()
        self.api_key_input.setPlaceholderText("请输入您的API Key")
        # 使用新的providers结构获取API key
        current_provider = self.config.get('current_provider', 'DeepSeek')
        if current_provider in self.config.get('providers', {}):
            api_key = self.config['providers'][current_provider].get('api_key', '')
        else:
            api_key = ''
        self.api_key_input.setText(api_key)
        self.api_key_input.setEchoMode(QLineEdit.Normal)
        self.api_key_input.setMinimumHeight(30)
        api_layout.addWidget(self.api_key_input)
        
        # API提供商选择
        provider_label = QLabel("API提供商:")
        provider_label.setFont(QFont("微软雅黑", 10, QFont.Bold))
        api_layout.addWidget(provider_label)
        
        self.provider_combo = QComboBox()
        self.provider_combo.addItems(["通义大模型", "DeepSeek", "腾讯混元"])
        index = self.provider_combo.findText(current_provider)
        if index >= 0:
            self.provider_combo.setCurrentIndex(index)
        self.provider_combo.currentTextChanged.connect(self.on_provider_changed)
        
        # 根据当前供应商设置对应的API key
        self.update_api_key_for_provider(current_provider)
        self.provider_combo.setMinimumHeight(30)
        # 禁用默认右键菜单
        self.provider_combo.setContextMenuPolicy(Qt.NoContextMenu)
        
        # 设置基本属性
        self.provider_combo.setEditable(False)  # 禁用编辑模式
        
        # 先添加下拉列表到布局
        api_layout.addWidget(self.provider_combo)
        
        # 为下拉列表添加底部间距
        spacer = QWidget()
        spacer.setFixedHeight(20)
        api_layout.addWidget(spacer)
        
        
        # Base URL输入
        base_url_label = QLabel("Base URL:")
        base_url_label.setFont(QFont("微软雅黑", 10, QFont.Bold))
        api_layout.addWidget(base_url_label)
        
        self.base_url_input = QLineEdit()
        self.base_url_input.setPlaceholderText("API基础URL")
        # 使用新的providers结构获取base_url
        if current_provider in self.config.get('providers', {}):
            base_url = self.config['providers'][current_provider].get('base_url', 'https://api.deepseek.com')
        else:
            base_url = 'https://api.deepseek.com'
        self.base_url_input.setText(base_url)
        self.base_url_input.setMinimumHeight(30)
        # 禁用默认右键菜单
        self.base_url_input.setContextMenuPolicy(Qt.NoContextMenu)
        api_layout.addWidget(self.base_url_input)
        
        # 模型选择
        model_label = QLabel("模型:")
        model_label.setFont(QFont("微软雅黑", 10, QFont.Bold))
        api_layout.addWidget(model_label)
        
        self.model_input = QLineEdit()
        self.model_input.setPlaceholderText("模型名称")
        # 使用新的providers结构获取model
        if current_provider in self.config.get('providers', {}):
            model = self.config['providers'][current_provider].get('model', 'deepseek-chat')
        else:
            model = 'deepseek-chat'
        self.model_input.setText(model)
        self.model_input.setMinimumHeight(30)
        # 禁用默认右键菜单
        self.model_input.setContextMenuPolicy(Qt.NoContextMenu)
        api_layout.addWidget(self.model_input)
        
        # 启用待办事项分析
        self.enable_ai_check = QCheckBox("启用待办事项分析功能")
        self.enable_ai_check.setChecked(self.config.get('enable_todo_analysis', False))
        # 禁用默认右键菜单
        self.enable_ai_check.setContextMenuPolicy(Qt.NoContextMenu)
        api_layout.addWidget(self.enable_ai_check)
        
        layout.addWidget(api_group)
        
        # 基本配置组
        basic_group = QGroupBox("基本配置")
        basic_layout = QVBoxLayout(basic_group)
        basic_layout.setSpacing(8)
        
        # 自动保存设置
        self.auto_save_check = QCheckBox("自动保存数据")
        self.auto_save_check.setChecked(self.config.get('auto_save', True))
        # 禁用默认右键菜单
        self.auto_save_check.setContextMenuPolicy(Qt.NoContextMenu)
        basic_layout.addWidget(self.auto_save_check)
        
        # 输出格式设置
        format_label = QLabel("输出格式:")
        format_label.setFont(QFont("微软雅黑", 10, QFont.Bold))
        basic_layout.addWidget(format_label)
        
        format_layout = QHBoxLayout()
        self.format_group = QButtonGroup()
        
        self.text_radio = QRadioButton("纯文本")
        self.text_radio.setContextMenuPolicy(Qt.NoContextMenu)
        self.json_radio = QRadioButton("JSON格式")
        self.json_radio.setContextMenuPolicy(Qt.NoContextMenu)
        self.table_radio = QRadioButton("表格格式")
        self.table_radio.setContextMenuPolicy(Qt.NoContextMenu)
        
        self.format_group.addButton(self.text_radio)
        self.format_group.addButton(self.json_radio)
        self.format_group.addButton(self.table_radio)
        
        format_layout.addWidget(self.text_radio)
        format_layout.addWidget(self.json_radio)
        format_layout.addWidget(self.table_radio)
        format_layout.addStretch()
        
        basic_layout.addLayout(format_layout)
        
        # 设置默认选中
        output_format = self.config.get('output_format', 'text')
        if output_format == 'text':
            self.text_radio.setChecked(True)
        elif output_format == 'json':
            self.json_radio.setChecked(True)
        elif output_format == 'table':
            self.table_radio.setChecked(True)
        
        # 主题设置
        theme_label = QLabel("界面主题:")
        theme_label.setFont(QFont("微软雅黑", 10, QFont.Bold))
        basic_layout.addWidget(theme_label)
        
        theme_layout = QHBoxLayout()
        self.theme_group = QButtonGroup()
        
        self.light_radio = QRadioButton("浅色主题")
        self.light_radio.setContextMenuPolicy(Qt.NoContextMenu)
        self.dark_radio = QRadioButton("深色主题")
        self.dark_radio.setContextMenuPolicy(Qt.NoContextMenu)
        
        self.theme_group.addButton(self.light_radio)
        self.theme_group.addButton(self.dark_radio)
        
        theme_layout.addWidget(self.light_radio)
        theme_layout.addWidget(self.dark_radio)
        theme_layout.addStretch()
        
        basic_layout.addLayout(theme_layout)
        
        # 设置默认选中
        theme = self.config.get('theme', 'light')
        if theme == 'light':
            self.light_radio.setChecked(True)
        else:
            self.dark_radio.setChecked(True)
        
        layout.addWidget(basic_group)
        
        layout.addStretch()
        
        # 保存按钮
        save_btn = QPushButton("保存配置")
        save_btn.setFixedSize(100, 35)
        save_btn.clicked.connect(self.save_config)
        # 禁用默认右键菜单
        save_btn.setContextMenuPolicy(Qt.NoContextMenu)
        layout.addWidget(save_btn, alignment=Qt.AlignCenter)
        
        # 添加底部间距，确保下拉列表有足够空间
        layout.addSpacing(20)
        
        self.setLayout(layout)
        
        # 初始化提供商设置
        self.on_provider_changed(current_provider)
        
    def update_api_key_for_provider(self, provider):
        """根据供应商更新API key"""
        if 'providers' in self.config:
            if provider == "DeepSeek" and 'DeepSeek' in self.config['providers']:
                self.api_key_input.setText(self.config['providers']['DeepSeek']['api_key'])
            elif provider == "通义大模型" and '通义大模型' in self.config['providers']:
                self.api_key_input.setText(self.config['providers']['通义大模型']['api_key'])
            elif provider == "腾讯混元" and '腾讯混元' in self.config['providers']:
                self.api_key_input.setText(self.config['providers']['腾讯混元']['api_key'])

        
    def on_provider_changed(self, provider):
        """提供商改变时的处理"""
        if provider == "DeepSeek":
            # 从配置中获取对应供应商的配置
            if 'providers' in self.config and 'DeepSeek' in self.config['providers']:
                provider_config = self.config['providers']['DeepSeek']
                self.api_key_input.setText(provider_config.get('api_key', ''))
                self.base_url_input.setText(provider_config.get('base_url', 'https://api.deepseek.com'))
                self.model_input.setText(provider_config.get('model', 'deepseek-chat'))
            else:
                # 使用默认值
                self.base_url_input.setText("https://api.deepseek.com")
                self.model_input.setText("deepseek-chat")
                self.api_key_input.setText("")
        elif provider == "通义大模型":
            # 从配置中获取对应供应商的配置
            if 'providers' in self.config and '通义大模型' in self.config['providers']:
                provider_config = self.config['providers']['通义大模型']
                self.api_key_input.setText(provider_config.get('api_key', ''))
                self.base_url_input.setText(provider_config.get('base_url', 'https://dashscope.aliyuncs.com/compatible-mode/v1'))
                self.model_input.setText(provider_config.get('model', 'qwen-plus'))
            else:
                # 使用默认值
                self.base_url_input.setText("https://dashscope.aliyuncs.com/compatible-mode/v1")
                self.model_input.setText("qwen-plus")
                self.api_key_input.setText("")
        elif provider == "腾讯混元":
            # 从配置中获取对应供应商的配置
            if 'providers' in self.config and '腾讯混元' in self.config['providers']:
                provider_config = self.config['providers']['腾讯混元']
                self.api_key_input.setText(provider_config.get('api_key', ''))
                self.base_url_input.setText(provider_config.get('base_url', 'https://api.hunyuan.cloud.tencent.com/v1'))
                self.model_input.setText(provider_config.get('model', 'hunyuan-turbos-latest'))
            else:
                # 使用默认值
                self.base_url_input.setText("https://api.hunyuan.cloud.tencent.com/v1")
                self.model_input.setText("hunyuan-turbos-latest")
                self.api_key_input.setText("")

            
    def save_config(self):
        """保存配置"""
        current_provider = self.provider_combo.currentText()
        
        # 确保providers配置存在
        if 'providers' not in self.config:
            self.config['providers'] = {}
        
        # 保存到当前提供商的配置中
        if current_provider not in self.config['providers']:
            self.config['providers'][current_provider] = {}
        
        self.config['providers'][current_provider].update({
            'api_key': self.api_key_input.text().strip(),
            'base_url': self.base_url_input.text().strip(),
            'model': self.model_input.text().strip()
        })
        
        # 保存其他配置
        self.config.update({
            'current_provider': current_provider,
            'api_provider': current_provider,
            'enable_todo_analysis': self.enable_ai_check.isChecked(),
            'auto_save': self.auto_save_check.isChecked(),
            'output_format': 'text' if self.text_radio.isChecked() else 
                           'json' if self.json_radio.isChecked() else 'table',
            'theme': 'light' if self.light_radio.isChecked() else 'dark'
        })
        
        self.accept()

    def show_config_context_menu(self, position):
        """显示配置对话框中文右键菜单"""
        context_menu = QMenu(self)
        
        # 重置配置
        reset_action = context_menu.addAction("重置配置")
        reset_action.triggered.connect(self.reset_config)
        
        # 导入配置
        import_action = context_menu.addAction("导入配置")
        import_action.triggered.connect(self.import_config)
        
        # 导出配置
        export_action = context_menu.addAction("导出配置")
        export_action.triggered.connect(self.export_config)
        
        context_menu.addSeparator()
        
        # 快速设置
        quick_deepseek = context_menu.addAction("快速设置 - DeepSeek")
        quick_deepseek.triggered.connect(lambda: self.quick_setup_provider("DeepSeek"))
        
        quick_hunyuan = context_menu.addAction("快速设置 - 腾讯混元")
        quick_hunyuan.triggered.connect(lambda: self.quick_setup_provider("腾讯混元"))
        
        quick_tongyi = context_menu.addAction("快速设置 - 通义大模型")
        quick_tongyi.triggered.connect(lambda: self.quick_setup_provider("通义大模型"))
        

        
        context_menu.exec_(self.mapToGlobal(position))

    def reset_config(self):
        """重置配置到默认值"""
        reply = self.show_centered_message(
            QMessageBox.Question,
            "确认重置", 
            "确定要重置所有配置到默认值吗？",
            QMessageBox.Yes | QMessageBox.No
        )
        if reply == QMessageBox.Yes:
            self.config = self.get_default_config()
            self.load_config_to_ui()
            self.show_centered_message(QMessageBox.Information, "成功", "配置已重置到默认值！")
            
    def import_config(self):
        """导入配置文件"""
        file_path, _ = QFileDialog.getOpenFileName(
            self, 
            "选择配置文件", 
            "", 
            "JSON文件 (*.json);;所有文件 (*)"
        )
        if file_path:
            try:
                with open(file_path, 'r', encoding='utf-8') as f:
                    imported_config = json.load(f)
                self.config.update(imported_config)
                self.load_config_to_ui()
                self.show_centered_message(QMessageBox.Information, "成功", "配置导入成功！")
            except Exception as e:
                self.show_centered_message(QMessageBox.Warning, "错误", f"导入配置失败：{str(e)}")
                
    def export_config(self):
        """导出配置文件"""
        file_path, _ = QFileDialog.getSaveFileName(
            self, 
            "保存配置文件", 
            "plugin_config.json", 
            "JSON文件 (*.json);;所有文件 (*)"
        )
        if file_path:
            try:
                with open(file_path, 'w', encoding='utf-8') as f:
                    json.dump(self.config, f, ensure_ascii=False, indent=2)
                self.show_centered_message(QMessageBox.Information, "成功", "配置导出成功！")
            except Exception as e:
                self.show_centered_message(QMessageBox.Warning, "错误", f"导出配置失败：{str(e)}")

    def show_centered_message(self, icon, title, text, buttons=QMessageBox.Ok):
        """显示居中的消息框"""
        msg_box = QMessageBox(icon, title, text, buttons, self)
        
        # 获取对话框几何信息
        dialog_rect = self.geometry()
        msg_box_size = msg_box.sizeHint()
        
        # 计算对话框中心位置
        center_x = dialog_rect.center().x() - msg_box_size.width() // 2
        center_y = dialog_rect.center().y() - msg_box_size.height() // 2
        
        # 确保窗口不会超出屏幕边界
        screen = QApplication.primaryScreen()
        if screen:
            screen_rect = screen.availableGeometry()
            center_x = max(screen_rect.left(), min(center_x, screen_rect.right() - msg_box_size.width()))
            center_y = max(screen_rect.top(), min(center_y, screen_rect.bottom() - msg_box_size.height()))
        
        # 设置消息框位置
        msg_box.move(center_x, center_y)
        
        # 执行消息框
        return msg_box.exec_()
        
    def quick_setup_provider(self, provider):
        """快速设置指定提供商"""
        if provider == "DeepSeek":
            self.provider_combo.setCurrentText("DeepSeek")
            self.base_url_input.setText("https://api.deepseek.com")
            self.model_input.setText("deepseek-chat")
            # 从配置中获取对应供应商的API key
            if 'providers' in self.config and 'DeepSeek' in self.config['providers']:
                self.api_key_input.setText(self.config['providers']['DeepSeek']['api_key'])
            self.enable_ai_check.setChecked(True)
        elif provider == "腾讯混元":
            self.provider_combo.setCurrentText("腾讯混元")
            self.base_url_input.setText("https://api.hunyuan.cloud.tencent.com/v1")
            self.model_input.setText("hunyuan-turbos-latest")
            # 从配置中获取对应供应商的API key
            if 'providers' in self.config and '腾讯混元' in self.config['providers']:
                self.api_key_input.setText(self.config['providers']['腾讯混元']['api_key'])
            self.enable_ai_check.setChecked(True)

        
        self.show_centered_message(QMessageBox.Information, "快速设置", f"{provider} 配置已快速设置完成！")

    def get_default_config(self):
        """获取默认配置"""
        return {
            'api_key': '',
            'api_provider': 'DeepSeek',
            'base_url': 'https://api.deepseek.com',
            'model': 'deepseek-chat',
            'enable_todo_analysis': False,
            'auto_save': True,
            'output_format': 'text',
            'theme': 'light'
        }
        
    def load_config_to_ui(self):
        """将配置加载到UI控件"""
        # API配置
        provider = self.config.get('current_provider', 'DeepSeek')
        
        # 从providers配置中加载当前提供商的配置
        if 'providers' in self.config and provider in self.config['providers']:
            provider_config = self.config['providers'][provider]
            self.api_key_input.setText(provider_config.get('api_key', ''))
            self.base_url_input.setText(provider_config.get('base_url', ''))
            self.model_input.setText(provider_config.get('model', ''))
        else:
            # 如果没有providers配置，使用旧的顶级配置
            self.api_key_input.setText(self.config.get('api_key', ''))
            self.base_url_input.setText(self.config.get('base_url', 'https://api.deepseek.com'))
            self.model_input.setText(self.config.get('model', 'deepseek-chat'))
        
        # 设置提供商选择
        index = self.provider_combo.findText(provider)
        if index >= 0:
            self.provider_combo.setCurrentIndex(index)
        
        # 确保API key能够正确显示
        self.update_api_key_for_provider(provider)
        
        self.enable_ai_check.setChecked(self.config.get('enable_todo_analysis', False))
        
        # 基本配置
        self.auto_save_check.setChecked(self.config.get('auto_save', True))
        output_format = self.config.get('output_format', 'text')
        if output_format == 'text':
            self.text_radio.setChecked(True)
        elif output_format == 'json':
            self.json_radio.setChecked(True)
        else:
            self.table_radio.setChecked(True)
        
        theme = self.config.get('theme', 'light')
        if theme == 'light':
            self.light_radio.setChecked(True)
        else:
            self.dark_radio.setChecked(True)
    
    def getInputs(self):
        """获取用户输入的内容和日期时间"""
        return self.content_edit.toPlainText(), self.datetime_edit.dateTime()
    
    def center_on_parent(self):
        """将窗口居中显示在父窗口中心"""
        if self.parent():
            # 获取父窗口几何信息
            parent_rect = self.parent().geometry()
            window_size = self.size()
            
            # 计算父窗口中心位置
            center_x = parent_rect.center().x() - window_size.width() // 2
            center_y = parent_rect.center().y() - window_size.height() // 2
            
            # 确保窗口不会超出屏幕边界
            screen = QApplication.primaryScreen()
            if screen:
                screen_rect = screen.availableGeometry()
                center_x = max(screen_rect.left(), min(center_x, screen_rect.right() - window_size.width()))
                center_y = max(screen_rect.top(), min(center_y, screen_rect.bottom() - window_size.height()))
            else:
                # 兼容旧版本Qt
                desktop = QApplication.desktop()
                screen_rect = desktop.availableGeometry()
                center_x = max(0, min(center_x, screen_rect.width() - window_size.width()))
                center_y = max(0, min(center_y, screen_rect.height() - window_size.height()))
            
            # 移动窗口到计算的位置
            self.move(center_x, center_y)
            print(f"配置窗口已居中显示在父窗口中心: ({center_x}, {center_y})")
    


class AIPluginWindow(QMainWindow):
    """AI插件主窗口"""
    
    def __init__(self, parent=None):
        super().__init__(parent)
        self.parent_window = parent
        self.data_list = []
        self.config = self.load_config()
        self.api_thread = None
        self.setup_ui()
        self.load_data()
        self.apply_theme()
        
        # 如果父窗口存在，确保窗口居中显示
        if parent:
            self.center_on_parent()
        
    def setup_ui(self):
        """设置主界面"""
        self.setWindowTitle("AI插件")
        self.setGeometry(100, 100, 800, 600)  # 增加初始窗口大小
        self.setMinimumSize(700, 500)  # 增加最小窗口大小
        
        # 设置窗口图标
        icon_path = get_icon_path()
        if icon_path and os.path.exists(icon_path):
            self.setWindowIcon(QIcon(icon_path))
            print(f"已设置窗口图标: {icon_path}")
        else:
            print(f"图标文件不存在: {icon_path}")
        
        # 设置中文右键菜单
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.show_context_menu)
        
        # 创建中央部件
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        
        # 主布局
        main_layout = QVBoxLayout(central_widget)
        main_layout.setSpacing(12)  # 增加间距，让布局更清晰
        main_layout.setContentsMargins(12, 12, 12, 12)  # 增加边距，让输入区域更突出
        
        # 输入区域 - 放在最顶部
        input_group = self.create_input_group()
        main_layout.addWidget(input_group)
        
        # 输出区域 - 自适应大小
        output_group = self.create_output_group()
        main_layout.addWidget(output_group, 1)  # 设置拉伸因子为1，让输出区域自适应
        
        # 按钮区域
        button_group = self.create_button_group()
        main_layout.addWidget(button_group)
        
        # 设置窗口大小策略，允许自适应
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        
        # 设置样式
        self.setStyleSheet("""
            QMainWindow {
                background-color: #f8f9fa;
            }
            QFrame {
                border: none;
                background-color: transparent;
            }
            QTextEdit {
                border: 1px solid #ced4da;
                border-radius: 4px;
                padding: 6px;
                background-color: white;
                font-family: '微软雅黑';
                font-size: 9pt;
            }
            QPushButton {
                background-color: #007bff;
                color: white;
                border: none;
                border-radius: 4px;
                padding: 8px 16px;
                font-weight: bold;
                font-size: 10pt;
            }
            QPushButton:hover {
                background-color: #0056b3;
            }
            QPushButton:pressed {
                background-color: #004085;
            }

            QLabel {
                color: #495057;
                font-weight: bold;
            }
            QGroupBox {
                font-weight: bold;
                border: 1px solid #dee2e6;
                border-radius: 6px;
                margin-top: 10px;
                padding-top: 10px;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 10px;
                padding: 0 5px 0 5px;
            }
            QLineEdit {
                border: 1px solid #ced4da;
                border-radius: 4px;
                padding: 4px;
                background-color: white;
            }
            QComboBox {
                border: 1px solid #ced4da;
                border-radius: 4px;
                padding: 4px;
                background-color: white;
            }
        """)
        
    def center_on_parent(self):
        """将窗口居中显示在父窗口中心"""
        if self.parent_window:
            # 获取父窗口几何信息
            parent_rect = self.parent_window.geometry()
            window_size = self.size()
            
            # 计算父窗口中心位置
            center_x = parent_rect.center().x() - window_size.width() // 2
            center_y = parent_rect.center().y() - window_size.height() // 2
            
            # 确保窗口不会超出屏幕边界
            screen = QApplication.primaryScreen()
            if screen:
                screen_rect = screen.availableGeometry()
                center_x = max(screen_rect.left(), min(center_x, screen_rect.right() - window_size.width()))
                center_y = max(screen_rect.top(), min(center_y, screen_rect.bottom() - window_size.height()))
            
            # 设置窗口位置
            self.move(center_x, center_y)
            print(f"AI插件窗口已居中显示在父窗口中心: ({center_x}, {center_y})")
    
    def show(self):
        """重写show方法，确保窗口居中显示"""
        super().show()
        # 在显示后再次确保居中
        if self.parent_window:
            self.center_on_parent()
        
    def create_input_group(self):
        """创建输入区域"""
        group_box = QFrame()
        
        layout = QVBoxLayout(group_box)
        layout.setSpacing(4)  # 减少间距，让文字更贴近顶部
        layout.setContentsMargins(0, 0, 0, 0)
        
        # 输入标签 - 顶部突出显示
        input_label = QLabel("输入内容:")
        input_label.setFont(QFont("微软雅黑", 11, QFont.Bold))  # 增加字体大小
        input_label.setContentsMargins(0, 0, 0, 4)  # 增加底部边距
        input_label.setStyleSheet("""
            QLabel {
                color: #2c3e50;
                margin: 0px;
                padding: 0px;
                background-color: transparent;
            }
        """)
        layout.addWidget(input_label)
        
        # 多行输入文本框 - 自适应宽度
        self.input_text = QTextEdit()
        self.input_text.setPlaceholderText("请在此输入您的内容...")
        self.input_text.setMinimumHeight(120)  # 增加最小高度
        self.input_text.setMaximumHeight(150)  # 增加最大高度
        self.input_text.setContentsMargins(8, 8, 8, 8)  # 增加内边距
        
        # 设置大小策略，让输入框自适应窗口宽度
        self.input_text.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        
        # 设置输入框样式，让它在顶部更突出
        self.input_text.setStyleSheet("""
            QTextEdit {
                border: 2px solid #3498db;
                border-radius: 6px;
                padding: 8px;
                background-color: white;
                font-family: '微软雅黑';
                font-size: 10pt;
                color: #2c3e50;
            }
            QTextEdit:focus {
                border: 2px solid #2980b9;
                background-color: #f8f9fa;
            }
        """)
        
        # 设置中文右键菜单
        self.input_text.setContextMenuPolicy(Qt.CustomContextMenu)
        self.input_text.customContextMenuRequested.connect(self.show_input_context_menu)
        
        layout.addWidget(self.input_text)
        
        return group_box
        
    def create_output_group(self):
        """创建输出区域"""
        group_box = QFrame()
        
        layout = QVBoxLayout(group_box)
        layout.setSpacing(4)  # 减少间距，让文字更贴近顶部
        layout.setContentsMargins(0, 0, 0, 0)
        
        # 输出标签
        output_label = QLabel("输出结果:")
        output_label.setFont(QFont("微软雅黑", 10, QFont.Bold))
        output_label.setContentsMargins(0, 0, 0, 0)  # 移除标签边距
        output_label.setStyleSheet("margin: 0px; padding: 0px;")  # 确保没有边距
        layout.addWidget(output_label)
        
        # 输出显示区域 - 自适应大小
        self.output_text = QTextEdit()
        self.output_text.setReadOnly(True)
        self.output_text.setMinimumHeight(150)
        
        # 设置大小策略，让输出框自适应窗口大小
        self.output_text.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        
        # 设置中文右键菜单
        self.output_text.setContextMenuPolicy(Qt.CustomContextMenu)
        self.output_text.customContextMenuRequested.connect(self.show_output_context_menu)
        self.output_text.setStyleSheet("""
            QTextEdit {
                background-color: #f8f9fa;
                border: 1px solid #dee2e6;
                border-radius: 4px;
                padding: 6px;
                font-family: 'Consolas', '微软雅黑';
                font-size: 9pt;
                color: #495057;
            }
        """)
        layout.addWidget(self.output_text)
        
        return group_box
        
    def create_button_group(self):
        """创建按钮区域"""
        group_box = QFrame()
        
        layout = QHBoxLayout(group_box)
        layout.setSpacing(12)
        layout.setContentsMargins(0, 0, 0, 0)
        
        # 分析按钮（第一步）
        self.analyze_button = QPushButton("分析")
        self.analyze_button.setFixedSize(100, 35)
        self.analyze_button.clicked.connect(self.analyze_input)
        layout.addWidget(self.analyze_button)
        
        # 添加按钮（第二步）
        self.add_button = QPushButton("添加")
        self.add_button.setFixedSize(100, 35)
        self.add_button.clicked.connect(self.add_data)
        self.add_button.setEnabled(False)  # 初始状态禁用
        layout.addWidget(self.add_button)
        
        # 测试按钮
        self.test_button = QPushButton("测试")
        self.test_button.setFixedSize(100, 35)
        self.test_button.setStyleSheet("""
            QPushButton {
                background-color: #ffc107;
                color: #212529;
                border: 1px solid #e0a800;
                border-radius: 4px;
                font-weight: bold;
            }
            QPushButton:hover {
                background-color: #e0a800;
                border-color: #d39e00;
            }
            QPushButton:pressed {
                background-color: #d39e00;
                border-color: #c69500;
            }
        """)
        self.test_button.clicked.connect(self.test_ai_model)
        layout.addWidget(self.test_button)
        
        # 配置按钮
        self.config_button = QPushButton("配置")
        self.config_button.setFixedSize(100, 35)
        self.config_button.clicked.connect(self.show_config)
        layout.addWidget(self.config_button)
        
        layout.addStretch()
        
        return group_box
        
    def analyze_input(self):
        """分析输入内容为待办事项（第一步）"""
        input_content = self.input_text.toPlainText().strip()
        if not input_content:
            QMessageBox.warning(self, "警告", "请输入内容后再分析！")
            return
            
        # 清空输出
        self.output_text.clear()
        
        # 简单的内容分析
        word_count = len(input_content.split())
        line_count = input_content.count('\n') + 1
        
        self.update_output(f"字符数: {len(input_content)} | 词数: {word_count} | 行数: {line_count}\n")
        
        # 内容类型判断
        if len(input_content) < 100:
            content_type = "短文本"
        else:
            content_type = "长文本"
            
        self.update_output(f"{content_type}\n\n")
        
        # 检查是否启用待办事项分析
        current_provider = self.config.get('current_provider', 'DeepSeek')
        if (self.config.get('enable_todo_analysis', False) and 
            'providers' in self.config and 
            current_provider in self.config['providers'] and
            self.config['providers'][current_provider].get('api_key', '').strip()):
            # 动态显示AI分析状态
            self.ai_status_label = QLabel("🤖 正在使用AI分析为待办事项...")
            self.ai_status_label.setStyleSheet("color: #007bff; font-weight: bold;")
            self.output_text.append("")  # 添加空行
            self.output_text.append("🤖 正在使用AI分析为待办事项...")
            self.perform_todo_analysis(input_content)
        else:
            self.update_output("✅ 基础分析完成！现在可以点击'添加'按钮保存内容。\n")
            self.update_output("💡 提示：在配置中启用AI分析可获得待办事项分析结果。\n")
            self.update_output("─" * 50 + "\n")
            # 启用添加按钮
            self.enable_add_button()
        
    def perform_todo_analysis(self, content):
        """执行待办事项分析"""
        try:
            # 获取当前提供商配置
            current_provider = self.config.get('current_provider', 'DeepSeek')
            provider_config = self.config['providers'][current_provider]
            
            # 创建API分析线程
            self.api_thread = APIAnalysisThread(
                api_key=provider_config.get('api_key', ''),
                base_url=provider_config.get('base_url', ''),
                model=provider_config.get('model', ''),
                content=content,
                provider=current_provider
            )
            
            # 连接信号
            self.api_thread.result_ready.connect(self.on_todo_analysis_complete)
            self.api_thread.error_occurred.connect(self.on_todo_analysis_error)
            
            # 启动线程
            self.api_thread.start()
            
        except Exception as e:
            self.update_output(f"❌ 待办事项分析启动失败: {str(e)}\n")
            self.enable_add_button()
            
    def on_todo_analysis_complete(self, result):
        """待办事项分析完成"""
        # 清空输出，只显示AI分析结果
        self.output_text.clear()
        
        # 只提取主要任务部分
        if "### 🎯 主要任务" in result:
            # 找到主要任务部分的开始
            start_index = result.find("### 🎯 主要任务")
            # 找到下一个标题的开始（如果有的话）
            next_section = result.find("### ", start_index + 1)
            if next_section != -1:
                # 提取到下一个标题之前的内容
                main_tasks = result[start_index:next_section].strip()
            else:
                # 如果没有下一个标题，提取到末尾
                main_tasks = result[start_index:].strip()
        else:
            # 如果没有找到主要任务标题，显示整个结果
            main_tasks = result
        
        self.update_output("🤖 待办事项分析结果:\n")
        self.update_output(main_tasks)
        self.update_output("\n✅ 待办事项分析完成！现在可以点击'添加'按钮保存内容。\n")
        self.update_output("─" * 50 + "\n")
        self.enable_add_button()
        
    def on_todo_analysis_error(self, error_msg):
        """待办事项分析出错"""
        # 清空输出，只显示错误信息
        self.output_text.clear()
        
        self.update_output(f"❌ 待办事项分析失败: {error_msg}\n")
        self.update_output("✅ 基础分析完成！现在可以点击'添加'按钮保存内容。\n")
        self.update_output("─" * 58 + "\n")
        self.enable_add_button()
        
    def enable_add_button(self):
        """启用添加按钮"""
        self.add_button.setEnabled(True)
        # 高亮添加按钮
        self.add_button.setStyleSheet("""
            QPushButton {
                background-color: #28a745;
                color: white;
                border: none;
                border-radius: 4px;
                padding: 8px 16px;
                font-weight: bold;
                font-size: 10pt;
            }
            QPushButton:hover {
                background-color: #218838;
            }
            QPushButton:pressed {
                background-color: #1e7e34;
            }
        """)
        
    def test_ai_model(self):
        """测试AI模型连接"""
        # 获取当前提供商
        current_provider = self.config.get('current_provider', 'DeepSeek')
        
        # 检查配置
        if 'providers' not in self.config or current_provider not in self.config['providers']:
            QMessageBox.warning(self, "警告", "请先配置API提供商！")
            return
            
        provider_config = self.config['providers'][current_provider]
        
        if not provider_config.get('api_key', '').strip():
            QMessageBox.warning(self, "警告", "请先配置API Key！")
            return
            
        if not provider_config.get('base_url', '').strip():
            QMessageBox.warning(self, "警告", "请先配置Base URL！")
            return
            
        if not provider_config.get('model', '').strip():
            QMessageBox.warning(self, "警告", "请先配置模型名称！")
            return
        
        # 清空输出
        self.output_text.clear()
        self.update_output("🧪 正在测试AI模型连接...\n")
        
        # 创建测试线程
        self.test_thread = APITestThread(
            api_key=provider_config.get('api_key', ''),
            base_url=provider_config.get('base_url', ''),
            model=provider_config.get('model', ''),
            provider=current_provider
        )
        
        # 连接信号
        self.test_thread.result_ready.connect(self.on_test_complete)
        self.test_thread.error_occurred.connect(self.on_test_error)
        
        # 启动线程
        self.test_thread.start()
        
    def on_test_complete(self, result):
        """测试完成"""
        self.output_text.clear()
        self.update_output("✅ AI模型测试成功！\n")
        self.update_output("─" * 50 + "\n")
        self.update_output(result)
        
    def on_test_error(self, error_msg):
        """测试出错"""
        self.output_text.clear()
        self.update_output("❌ AI模型测试失败！\n")
        self.update_output("─" * 50 + "\n")
        self.update_output(f"错误信息: {error_msg}\n")
        self.update_output("请检查以下配置：\n")
        self.update_output("1. API Key是否正确\n")
        self.update_output("2. Base URL是否正确\n")
        self.update_output("3. 模型名称是否正确\n")
        self.update_output("4. 网络连接是否正常\n")
        
    def add_data(self):
        """添加数据功能（第二步）"""
        # 获取输出区域中的AI分析结果
        output_content = self.output_text.toPlainText().strip()
        if not output_content:
            QMessageBox.warning(self, "警告", "请先进行分析后再添加！")
            return
            
        # 提取"### 🎯 主要任务"下面的内容
        if "### 🎯 主要任务" in output_content:
            start_index = output_content.find("### 🎯 主要任务")
            # 找到下一个标题的开始（如果有的话）
            next_section = output_content.find("### ", start_index + 1)
            if next_section != -1:
                # 提取到下一个标题之前的内容
                main_tasks = output_content[start_index:next_section].strip()
            else:
                # 如果没有下一个标题，提取到末尾
                main_tasks = output_content[start_index:].strip()
            
            # 去掉标题行和状态提示信息，只保留任务内容
            lines = main_tasks.split('\n')
            filtered_lines = []
            for line in lines:
                # 跳过标题行
                if line.startswith('###'):
                    continue
                # 跳过状态提示信息
                if '✅ 待办事项分析完成！' in line or "现在可以点击'添加'按钮保存内容" in line:
                    continue
                # 跳过分隔线
                if line.startswith('─') or line.startswith('🤖 待办事项分析结果:'):
                    continue
                filtered_lines.append(line)
            task_content = '\n'.join(filtered_lines).strip()
        else:
            # 如果没有找到主要任务标题，使用整个输出内容
            task_content = output_content
            
        if not task_content:
            QMessageBox.warning(self, "警告", "没有找到可添加的任务内容！")
            return
            
        try:
            # 弹出完成时间选择对话框
            dialog = CompletionTimeDialog(self)
            if dialog.exec() == QDialog.Accepted:
                # 检查是否是取消但不设置时间的情况
                if dialog.cancel_without_time_flag:
                    # 取消但不设置时间，due_date为None
                    due_date = None
                else:
                    # 正常确定，获取选择的完成时间
                    due_date = dialog.datetime_edit.dateTime().toString("yyyy-MM-dd HH:mm") if dialog.datetime_edit.dateTime().isValid() else None
                
                # 获取当前时间
                current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                
                # 将任务内容插入todos表
                if self.parent_window and hasattr(self.parent_window, 'db_file'):
                    # 使用父窗口的数据库连接
                    conn = sqlite3.connect(self.parent_window.db_file)
                    cursor = conn.cursor()
                    
                    # 添加待办事项
                    cursor.execute('''
                        INSERT INTO todos (content, datetime, completed, important, due_date)
                        VALUES (?, ?, ?, ?, ?)
                    ''', (task_content, current_time, 0, 0, due_date))
                
                # 获取插入的ID
                item_id = cursor.lastrowid
                
                # 更新数据库版本号
                current_time_full = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                cursor.execute('''
                    UPDATE database_version 
                    SET version = version + 1, last_updated = ?
                    WHERE id = 1
                ''', (current_time_full,))
                
                # 提交事务
                conn.commit()
                conn.close()
                
                # 刷新父窗口的待办列表
                if hasattr(self.parent_window, 'loadData'):
                    self.parent_window.loadData()
                
                # 更新输出显示
                self.update_output(f"💾 已添加新待办事项 (ID: {item_id})\n")
                self.update_output(f"📝 内容: {task_content[:50]}{'...' if len(task_content) > 50 else ''}\n")
                self.update_output(f"⏰ 时间: {current_time}\n")
                self.update_output(f"⏳ 完成时间: {due_date if due_date else '未设置'}\n")
                self.update_output(f"📊 状态: 未完成\n")
                
                # 显示成功消息
                QMessageBox.information(self, "成功", f"待办事项已成功添加到数据库！\n内容: {task_content[:100]}{'...' if len(task_content) > 100 else ''}")
                
                # 触发同步功能
                if self.parent_window and hasattr(self.parent_window, 'auto_upload_to_remote'):
                    try:
                        print("🔄 AI插件数据导入完成，触发同步功能...")
                        self.parent_window.auto_upload_to_remote()
                        print("✅ 同步功能已触发")
                    except Exception as e:
                        print(f"⚠️ 触发同步功能时出错: {e}")
                
                # 清空输入框
                self.input_text.clear()
                
                # 重置按钮状态
                self.add_button.setEnabled(False)
                self.add_button.setStyleSheet("""
                    QPushButton {
                        background-color: #007bff;
                        color: white;
                        border: none;
                        border-radius: 4px;
                        padding: 8px 16px;
                        font-weight: bold;
                        font-size: 10pt;
                    }
                    QPushButton:hover {
                        background-color: #0056b3;
                    }
                    QPushButton:pressed {
                        background-color: #004085;
                    }
                """)
                
            else:
                QMessageBox.warning(self, "错误", "无法连接到主程序数据库")
                
        except Exception as e:
            QMessageBox.critical(self, "错误", f"添加待办事项到数据库失败: {str(e)}")
            print(f"数据库插入失败: {str(e)}")
        
    def show_centered_message(self, icon, title, text, buttons=QMessageBox.Ok):
        """显示居中的消息框"""
        msg_box = QMessageBox(icon, title, text, buttons, self)
        
        # 获取窗口几何信息
        window_rect = self.geometry()
        msg_box_size = msg_box.sizeHint()
        
        # 计算窗口中心位置
        center_x = window_rect.center().x() - msg_box_size.width() // 2
        center_y = window_rect.center().y() - msg_box_size.height() // 2
        
        # 确保窗口不会超出屏幕边界
        screen = QApplication.primaryScreen()
        if screen:
            screen_rect = screen.availableGeometry()
            center_x = max(screen_rect.left(), min(center_x, screen_rect.right() - msg_box_size.width()))
            center_y = max(screen_rect.top(), min(center_y, screen_rect.bottom() - msg_box_size.height()))
        
        # 设置消息框位置
        msg_box.move(center_x, center_y)
        
        # 执行消息框
        return msg_box.exec_()
    
    def show_config(self):
        """显示配置界面"""
        if self.parent_window:
            self.parent_window.show_plugin_config()
        else:
            # 如果没有父窗口，创建独立的配置对话框
            dialog = PluginConfigDialog(self, self.config)
            
            # 确保配置窗口始终保持在AI插件界面中心区域
            # 获取AI插件窗口几何信息
            plugin_rect = self.geometry()
            dialog_size = dialog.size()
            
            # 计算AI插件窗口中心位置
            center_x = plugin_rect.center().x() - dialog_size.width() // 2
            center_y = plugin_rect.center().y() - dialog_size.height() // 2
            
            # 确保窗口不会超出屏幕边界
            screen = QApplication.primaryScreen()
            if screen:
                screen_rect = screen.availableGeometry()
                center_x = max(screen_rect.left(), min(center_x, screen_rect.right() - dialog_size.width()))
                center_y = max(screen_rect.top(), min(center_y, screen_rect.bottom() - dialog_size.height()))
            
            # 设置窗口位置
            dialog.move(center_x, center_y)
            
            if dialog.exec_() == QDialog.Accepted:
                self.config = dialog.config
                self.save_config_file()
                # 立即应用新配置
                self.load_config()
                self.apply_theme()
                
                # 显示居中的成功消息框
                self.show_centered_message(QMessageBox.Information, "成功", "配置已保存并立即生效！")
            
    def update_output(self, text):
        """更新输出显示"""
        self.output_text.append(text)
        
    def load_data(self):
        """从数据库加载数据"""
        try:
            if self.parent_window and hasattr(self.parent_window, 'db_file'):
                # 使用父窗口的数据库连接
                conn = sqlite3.connect(self.parent_window.db_file)
                cursor = conn.cursor()
                
                # 从数据库加载数据到内存列表
                cursor.execute("SELECT * FROM todos ORDER BY datetime DESC")
                rows = cursor.fetchall()
                
                self.data_list = []
                for row in rows:
                    data_item = {
                        'id': row[0],
                        'content': row[1],
                        'datetime': row[2],
                        'completed': row[3],
                        'important': row[4]
                    }
                    self.data_list.append(data_item)
                    
                print(f"从数据库加载了 {len(self.data_list)} 条待办事项")
                conn.close()
                
        except Exception as e:
            print(f"从数据库加载数据失败: {str(e)}")
            QMessageBox.critical(self, "错误", f"从数据库加载数据失败: {str(e)}")
            
    def load_config(self):
        """加载配置文件"""
        try:
            config_path = get_config_file_path()
            print(f"尝试加载配置文件: {config_path}")
            if os.path.exists(config_path):
                with open(config_path, 'r', encoding='utf-8') as f:
                    config = json.load(f)
                    # 确保配置结构完整
                    if 'providers' not in config:
                        config['providers'] = {}
                    if 'current_provider' not in config:
                        config['current_provider'] = config.get('api_provider', 'DeepSeek')
                    print(f"成功加载配置文件，当前提供商: {config.get('current_provider')}")
                    return config
            else:
                print(f"配置文件不存在: {config_path}")
        except Exception as e:
            print(f"加载配置文件失败: {e}")
            pass
        
        # 返回默认配置
        return {
            'current_provider': 'DeepSeek',
            'providers': {
                'DeepSeek': {
                    'api_key': '',
                    'base_url': 'https://api.deepseek.com',
                    'model': 'deepseek-chat'
                }
            },
            'api_provider': 'DeepSeek',
            'enable_todo_analysis': False,
            'auto_save': True,
            'output_format': 'text',
            'theme': 'light'
        }
        
    def save_config_file(self):
        """保存配置文件"""
        try:
            config_path = get_config_file_path()
            print(f"保存配置文件到: {config_path}")
            with open(config_path, 'w', encoding='utf-8') as f:
                json.dump(self.config, f, ensure_ascii=False, indent=2)
            print("配置文件保存成功")
        except Exception as e:
            print(f"保存配置文件失败: {e}")
            QMessageBox.critical(self, "错误", f"保存配置失败: {str(e)}")
            
    def apply_theme(self):
        """应用主题设置"""
        theme = self.config.get('theme', 'light')
        if theme == 'dark':
            self.setStyleSheet("""
                QMainWindow {
                    background-color: #2d3748;
                    color: #e2e8f0;
                }
                QFrame {
                    border: none;
                    background-color: transparent;
                }
                QTextEdit {
                    border: 1px solid #4a5568;
                    border-radius: 4px;
                    padding: 6px;
                    background-color: #4a5568;
                    color: #e2e8f0;
                }
                QPushButton {
                    background-color: #3182ce;
                    color: white;
                    border: none;
                    border-radius: 4px;
                    padding: 8px 16px;
                    font-weight: bold;
                }
                QPushButton:hover {
                    background-color: #2c5aa0;
                }
                QLabel {
                    color: #e2e8f0;
                }
                QGroupBox {
                    font-weight: bold;
                    border: 1px solid #4a5568;
                    border-radius: 6px;
                    margin-top: 10px;
                    padding-top: 10px;
                    color: #e2e8f0;
                }
                QLineEdit {
                    border: 1px solid #4a5568;
                    border-radius: 4px;
                    padding: 4px;
                    background-color: #4a5568;
                    color: #e2e8f0;
                }
                QComboBox {
                    border: 1px solid #4a5568;
                    border-radius: 4px;
                    padding: 4px;
                    background-color: #4a5568;
                    color: #e2e8f0;
                }
            """)
        else:
            # 浅色主题（默认）
            self.setStyleSheet("""
                QMainWindow {
                    background-color: #f8f9fa;
                }
                QFrame {
                    border: none;
                    background-color: transparent;
                }
                QTextEdit {
                    border: 1px solid #ced4da;
                    border-radius: 4px;
                    padding: 6px;
                    background-color: white;
                    font-family: '微软雅黑';
                    font-size: 9pt;
                }
                QPushButton {
                    background-color: #007bff;
                    color: white;
                    border: none;
                    border-radius: 4px;
                    padding: 8px 16px;
                    font-weight: bold;
                    font-size: 10pt;
                }
                QPushButton:hover {
                    background-color: #0056b3;
                }
                QPushButton:pressed {
                    background-color: #004085;
                }
                QLabel {
                    color: #495057;
                    font-weight: bold;
                }
                QGroupBox {
                    font-weight: bold;
                    border: 1px solid #dee2e6;
                    border-radius: 6px;
                    margin-top: 10px;
                    padding-top: 10px;
                }
                QGroupBox::title {
                    subcontrol-origin: margin;
                    left: 10px;
                    padding: 0 5px 0 5px;
                }
                QLineEdit {
                    border: 1px solid #ced4da;
                    border-radius: 4px;
                    padding: 4px;
                    background-color: white;
                }
                QComboBox {
                    border: 1px solid #ced4da;
                    border-radius: 4px;
                    padding: 4px;
                    background-color: white;
                }
            """)

    def show_context_menu(self, position):
        """显示中文右键菜单"""
        context_menu = QMenu(self)
        
        # 复制菜单项
        copy_action = context_menu.addAction("复制")
        copy_action.triggered.connect(self.copy_selected_text)
        
        # 粘贴菜单项
        paste_action = context_menu.addAction("粘贴")
        paste_action.triggered.connect(self.paste_text)
        
        # 全选菜单项
        select_all_action = context_menu.addAction("全选")
        select_all_action.triggered.connect(self.select_all_text)
        
        # 清空菜单项
        clear_action = context_menu.addAction("清空")
        clear_action.triggered.connect(self.clear_text)
        
        # 分隔线
        context_menu.addSeparator()
        
        # 字体设置菜单项
        font_action = context_menu.addAction("字体设置")
        font_action.triggered.connect(self.show_font_dialog)
        
        # 显示菜单
        context_menu.exec_(self.mapToGlobal(position))

    def show_input_context_menu(self, position):
        """显示输入框中文右键菜单"""
        context_menu = QMenu(self.input_text)
        
        # 撤销
        undo_action = context_menu.addAction("撤销")
        undo_action.triggered.connect(self.input_text.undo)
        
        # 重做
        redo_action = context_menu.addAction("重做")
        redo_action.triggered.connect(self.input_text.redo)
        
        context_menu.addSeparator()
        
        # 剪切
        cut_action = context_menu.addAction("剪切")
        cut_action.triggered.connect(self.input_text.cut)
        
        # 复制
        copy_action = context_menu.addAction("复制")
        copy_action.triggered.connect(self.input_text.copy)
        
        # 粘贴
        paste_action = context_menu.addAction("粘贴")
        paste_action.triggered.connect(self.input_text.paste)
        
        context_menu.addSeparator()
        
        # 全选
        select_all_action = context_menu.addAction("全选")
        select_all_action.triggered.connect(self.input_text.selectAll)
        
        # 清空
        clear_action = context_menu.addAction("清空")
        clear_action.triggered.connect(self.input_text.clear)
        
        context_menu.addSeparator()
        
        # 字体设置
        font_action = context_menu.addAction("字体设置")
        font_action.triggered.connect(lambda: self.show_font_dialog_for_widget(self.input_text))
        
        context_menu.exec_(self.input_text.mapToGlobal(position))

    def show_output_context_menu(self, position):
        """显示输出框中文右键菜单"""
        context_menu = QMenu(self.output_text)
        
        # 复制
        copy_action = context_menu.addAction("复制")
        copy_action.triggered.connect(self.output_text.copy)
        
        # 全选
        select_all_action = context_menu.addAction("全选")
        select_all_action.triggered.connect(self.output_text.selectAll)
        
        # 清空
        clear_action = context_menu.addAction("清空")
        clear_action.triggered.connect(self.output_text.clear)
        
        # 分隔线
        context_menu.addSeparator()
        
        # 字体设置
        font_action = context_menu.addAction("字体设置")
        font_action.triggered.connect(lambda: self.show_font_dialog_for_widget(self.output_text))
        
        # 保存到文件
        save_action = context_menu.addAction("保存到文件")
        save_action.triggered.connect(self.save_output_to_file)
        
        context_menu.exec_(self.output_text.mapToGlobal(position))

    def copy_selected_text(self):
        """复制选中的文本"""
        clipboard = QApplication.clipboard()
        clipboard.clear()
        clipboard.setText(self.get_selected_text())
        
    def paste_text(self):
        """粘贴文本到当前焦点控件"""
        clipboard = QApplication.clipboard()
        text = clipboard.text()
        if text:
            current_widget = self.focusWidget()
            if hasattr(current_widget, 'insertPlainText'):
                current_widget.insertPlainText(text)
            elif hasattr(current_widget, 'setText'):
                current_widget.setText(text)
                
    def select_all_text(self):
        """全选当前焦点控件的文本"""
        current_widget = self.focusWidget()
        if hasattr(current_widget, 'selectAll'):
            current_widget.selectAll()
            
    def clear_text(self):
        """清空当前焦点控件的文本"""
        current_widget = self.focusWidget()
        if hasattr(current_widget, 'clear'):
            current_widget.clear()
        elif hasattr(current_widget, 'setText'):
            current_widget.setText('')
            
    def get_selected_text(self):
        """获取选中的文本"""
        current_widget = self.focusWidget()
        if hasattr(current_widget, 'textCursor'):
            cursor = current_widget.textCursor()
            return cursor.selectedText()
        else:
            return ''
        
    def show_font_dialog(self):
        """显示字体设置对话框"""
        font, ok = QFontDialog.getFont(self.font(), self, "选择字体")
        if ok:
            self.setFont(font)
            # 同时设置输入和输出框的字体
            self.input_text.setFont(font)
            self.output_text.setFont(font)

    def show_font_dialog_for_widget(self, widget):
        """为指定控件显示字体设置对话框"""
        font, ok = QFontDialog.getFont(widget.font(), self, "选择字体")
        if ok:
            widget.setFont(font)
            
    def save_output_to_file(self):
        """保存输出内容到文件"""
        file_path, _ = QFileDialog.getSaveFileName(
            self, 
            "保存输出内容", 
            "", 
            "文本文件 (*.txt);;所有文件 (*)"
        )
        if file_path:
            try:
                with open(file_path, 'w', encoding='utf-8') as f:
                    f.write(self.output_text.toPlainText())
                QMessageBox.information(self, "成功", "输出内容已保存到文件！")
            except Exception as e:
                QMessageBox.warning(self, "错误", f"保存文件失败：{str(e)}")
                
    def closeEvent(self, event):
        """程序关闭时的事件处理"""
        try:
            # 关闭数据库连接
            if hasattr(self, 'conn'):
                self.conn.close()
                print("数据库连接已关闭")
        except Exception as e:
            print(f"关闭数据库连接失败: {str(e)}")
        
        event.accept()

class MainWindow(QMainWindow):
    """主窗口类"""
    def __init__(self, username=None):
        super().__init__()
        self.username = username or "default"
        
        # 设置图标
        icon_path = APP_ICON
        if icon_path and os.path.exists(icon_path):
            icon = QIcon(icon_path)
            self.setWindowIcon(icon)
            print(f"已设置主窗口图标: {icon_path}")
        else:
            print("未加载自定义图标，使用系统默认图标")
        
        # 数据库文件路径
        self.db_file = None
        
        # 数据列表
        self.todo_list = []
        self.completed_list = []
        
        # 自动上传相关
        self.auto_upload_enabled = True
        self.last_upload_time = None
        self.auto_upload_worker = None
        
        # 上传进度提示标签
        self.upload_progress_label = None
        
        # 进度更新节流机制
        self.last_progress_update_time = 0
        self.progress_update_interval = 0.1  # 100ms更新一次
        
        # 提醒定时器
        self.reminder_timer = QTimer()
        self.reminder_timer.timeout.connect(self.checkDueItems)
        self.reminder_timer.start(60000)  # 每分钟检查一次
        print("提醒定时器已启动，每分钟检查一次")
        
        # 已提醒的事项记录，防止重复提醒
        self.reminded_items = set()
        
        # 界面刷新定时器，每10秒更新一次时间显示
        self.ui_refresh_timer = QTimer()
        self.ui_refresh_timer.timeout.connect(self.refreshTimeDisplay)
        self.ui_refresh_timer.start(10000)  # 每10秒刷新一次，确保超时时间及时更新
        
        # 窗口状态
        self.is_locked = False
        self.is_dragging = False
        self.drag_position = None
        
        # 窗口大小调整
        self.resizing = False
        self.resize_start_position = None
        self.start_geometry = None
        
        # 密码管理
        self.password_manager = PasswordManager()
        self.is_interface_locked = False
        
        if not self.password_manager.has_password():
            self.is_interface_locked = False
            print("密码文件不存在，桌面已自动解锁")
        
        # 自动隐藏相关 - 优化配置
        self.is_hidden = False
        self.auto_hide_threshold = 0  # 贴边阈值，改为0表示必须完全贴边
        self.original_geometry = None
        
        # 精确的鼠标贴边检测配置
        self.edge_detection_margin = 0  # 贴边检测边距（像素），改为0表示必须完全贴边
        self.hover_detection_margin = 0  # 悬停检测边距（像素），改为0表示必须完全贴边
        self.edge_hover_delay = 100  # 贴边悬停延迟（毫秒）
        self.edge_leave_delay = 300  # 离开贴边区域延迟（毫秒）
        
        # 自动隐藏检测定时器
        self.auto_hide_timer = QTimer(self)
        self.auto_hide_timer.timeout.connect(self.check_auto_hide)
        self.auto_hide_timer.start(50)  # 每50ms检查一次，提高响应速度
        
        # 动画相关
        self.hide_animation = None
        self.show_animation = None
        self.animation_duration = 400  # 减少动画时间，提高响应速度
        
        # 鼠标悬停状态 - 优化状态管理
        self.mouse_hovering = False
        self.hover_start_time = None
        self.user_interacting = False
        self.edge_hovering = False  # 新增：贴边悬停状态
        self.last_edge_position = None  # 新增：上次贴边位置
        
        # 鼠标悬停检测定时器
        self.hover_timer = QTimer(self)
        self.hover_timer.setSingleShot(True)
        self.hover_timer.timeout.connect(self.on_hover_timer_timeout)
        
        # 鼠标位置检测定时器
        self.mouse_check_timer = QTimer(self)
        self.mouse_check_timer.timeout.connect(self.check_mouse_hover)
        self.mouse_check_timer.start(50)  # 每50ms检查一次
        
        # 启用鼠标跟踪
        self.setMouseTracking(True)
        
        # 初始化自定义tooltip
        self.custom_tooltip = None
        
        # 初始化设置
        self.settings = QSettings("TodoApp", "Settings")
        
        # 初始化各个模块
        self.init_system_tray()
        self.init_database()
        self.initUI()
        self.loadData()
        self.restoreWindowState()
        
        # 初始化插件刷新监控
        self.init_plugin_refresh_monitor()
        
        # 初始化AI插件进程
        self.ai_plugin_process = None
        
        # 初始化待更新信息
        self.pending_update_info = None
        
        # 版本检查
        self.check_version_on_startup()
        
        # 定时版本检查 - 改为半小时检查一次
        self.version_check_timer = QTimer(self)
        self.version_check_timer.timeout.connect(self.periodic_version_check)
        self.version_check_timer.start(30 * 60 * 1000)  # 30分钟检查一次
        
        # 软件版本更新检查 - 启动时和每半小时检查一次
        self.software_update_timer = QTimer(self)
        self.software_update_timer.timeout.connect(self.check_software_update_silent)
        self.software_update_timer.start(30 * 60 * 1000)  # 30分钟检查一次
        
        # 启动时检查软件更新
        QTimer.singleShot(3000, self.check_software_update_silent)  # 延迟3秒后检查
        
        # 窗口状态保存定时器
        self.saveStateTimer = QTimer(self)
        self.saveStateTimer.timeout.connect(self.saveWindowState)
        self.saveStateTimer.start(30 * 1000)  # 每30秒保存一次窗口状态
    
    def init_system_tray(self):
        """初始化系统托盘"""
        # 创建系统托盘图标
        self.tray_icon = QSystemTrayIcon(self)
        
        # 设置托盘图标
        icon_path = get_icon_path()
        if icon_path and os.path.exists(icon_path):
            self.tray_icon.setIcon(QIcon(icon_path))
        else:
            # 如果没有图标文件，使用默认图标
            self.tray_icon.setIcon(self.style().standardIcon(self.style().SP_ComputerIcon))
        
        # 设置托盘提示
        self.tray_icon.setToolTip(f"待办事项管理软件 {APP_VERSION}")
        
        # 创建托盘菜单
        self.tray_menu = QMenu()
        
        # 显示主窗口动作
        show_action = QAction("显示主窗口", self)
        show_action.triggered.connect(self.show_from_tray)
        self.tray_menu.addAction(show_action)
        
        # 添加分隔线
        self.tray_menu.addSeparator()
        
        # 退出动作
        quit_action = QAction("退出", self)
        quit_action.triggered.connect(self.closeAndQuit)
        self.tray_menu.addAction(quit_action)
        
        # 设置托盘菜单
        self.tray_icon.setContextMenu(self.tray_menu)
        
        # 连接托盘图标点击事件
        self.tray_icon.activated.connect(self.tray_icon_activated)
        
        # 显示托盘图标
        self.tray_icon.show()
        
        print("✅ 系统托盘已初始化")
    
    def show_from_tray(self):
        """从托盘显示主窗口"""
        if self.is_hidden:
            self.show_full_window()
        else:
            self.show()
            self.raise_()
            self.activateWindow()
    
    def tray_icon_activated(self, reason):
        """托盘图标被激活时的处理"""
        if reason == QSystemTrayIcon.DoubleClick:
            # 双击托盘图标显示主窗口
            self.show_from_tray()
    
    def hide_to_tray(self):
        """隐藏到托盘"""
        # 不隐藏窗口，只显示托盘消息
        self.tray_icon.showMessage(
            "待办事项管理软件",
            "软件已隐藏到系统托盘",
            QSystemTrayIcon.Information,
            2000
        )
    
    def auto_upload_to_remote(self):
        """自动上传到远程服务器"""
        try:
            print("=== 自动上传检查开始 ===")
            
            # 显示进度提示
            self.show_upload_progress("正在检查自动上传...")
            
            # 检查是否有WebDAV配置
            settings = QSettings("TodoApp", "WebDAVSync")
            webdav_url = settings.value("url", "")
            webdav_username = settings.value("username", "")
            encrypted_password = settings.value("password", "")
            webdav_remote_path = settings.value("remote_path", "")
            
            print(f"WebDAV配置: URL={webdav_url}, 用户名={webdav_username}, 远程路径={webdav_remote_path}")
            
            # 解密密码
            if encrypted_password:
                from webdav_sync import decrypt_password
                webdav_password = decrypt_password(encrypted_password)
                print("✓ 密码解密成功")
            else:
                webdav_password = ""
                print("✗ 未找到加密密码")
                self.hide_upload_progress()
                return
            
            # 如果没有配置WebDAV，直接返回
            if not webdav_url or not webdav_username or not webdav_password:
                print("✗ WebDAV配置不完整，跳过自动上传")
                self.hide_upload_progress()
                return
            
            print("✓ WebDAV配置完整")
            self.show_upload_progress("正在检查版本号...")
            
            # 获取当前时间用于记录上传时间
            current_time = QDateTime.currentDateTime()
            
            # 获取本地版本号
            local_version = self.get_local_version()
            if local_version is None:
                print("✗ 无法获取本地版本号，跳过自动上传")
                self.hide_upload_progress()
                return
            
            print(f"✓ 本地版本号: {local_version}")
            
            # 获取远程版本号
            remote_version = self.get_remote_version(webdav_url, webdav_username, webdav_password, webdav_remote_path)
            print(f"✓ 远程版本号: {remote_version}")
            
            # 如果本地版本号大于远程版本号，自动上传
            if remote_version is not None and local_version > remote_version:
                print(f"🚀 检测到版本号变化，开始自动上传: 本地={local_version}, 远程={remote_version}")
                self.show_upload_progress("正在上传到云端...")
                
                # 创建上传工作线程
                from webdav_sync import WebDAVWorker
                self.auto_upload_worker = WebDAVWorker(
                    webdav_url, webdav_username, webdav_password, 
                    self.db_file, "upload", webdav_remote_path
                )
                self.auto_upload_worker.finished_signal.connect(self.on_auto_upload_finished)
                self.auto_upload_worker.start()
                
                # 更新最后上传时间
                self.last_upload_time = current_time
                print("✅ 自动上传线程已启动")
            else:
                print("ℹ 版本号无需更新，跳过自动上传")
                self.hide_upload_progress()
                
        except Exception as e:
            print(f"❌ 自动上传时出错: {e}")
            import traceback
            traceback.print_exc()
            self.hide_upload_progress()
    
    def periodic_version_check(self):
        """定时检查版本号，如果本地版本号高于远端，触发自动备份"""
        try:
            print("=== 定时版本号检查开始 ===")
            
            # 检查是否有WebDAV配置
            settings = QSettings("TodoApp", "WebDAVSync")
            webdav_url = settings.value("url", "")
            webdav_username = settings.value("username", "")
            encrypted_password = settings.value("password", "")
            webdav_remote_path = settings.value("remote_path", "")
            
            # 如果没有配置WebDAV，直接返回
            if not webdav_url or not webdav_username or not encrypted_password:
                print("ℹ 未配置WebDAV，跳过定时版本号检查")
                return
            
            # 解密密码
            from webdav_sync import decrypt_password
            webdav_password = decrypt_password(encrypted_password)
            
            # 获取本地版本号
            local_version = self.get_local_version()
            if local_version is None:
                print("✗ 无法获取本地版本号，跳过定时版本号检查")
                return
            
            # 获取远程版本号
            remote_version = self.get_remote_version(webdav_url, webdav_username, webdav_password, webdav_remote_path)
            if remote_version is None:
                print("✗ 无法获取远程版本号，跳过定时版本号检查")
                return
            
            print(f"📊 定时检查 - 本地版本号: {local_version}, 远程版本号: {remote_version}")
            
            # 如果本地版本号大于远程版本号，触发自动备份
            if local_version > remote_version:
                print(f"🚀 定时检查发现版本号差异，触发自动备份: 本地={local_version}, 远程={remote_version}")
                self.auto_upload_to_remote()
            else:
                print("ℹ 定时检查 - 版本号无需更新")
                
        except Exception as e:
            print(f"❌ 定时版本号检查时出错: {e}")
            import traceback
            traceback.print_exc()
        
        print("=== 定时版本号检查结束 ===")
    
    def on_auto_upload_finished(self, success, message):
        """自动上传完成回调"""
        if success:
            print(f"✅ 自动上传成功: {message}")
            self.show_upload_progress("上传成功！", 2000)  # 显示2秒
        else:
            print(f"❌ 自动上传失败: {message}")
            self.show_upload_progress("上传失败！", 3000)  # 显示3秒
        print("=== 自动上传检查结束 ===")
    
    def show_upload_progress(self, message, duration=0):
        """显示上传进度提示"""
        try:
            # 如果还没有创建进度标签，创建一个
            if not self.upload_progress_label:
                self.upload_progress_label = QLabel(self)
                self.upload_progress_label.setStyleSheet("""
                    QLabel {
                        background-color: rgba(0, 0, 0, 180);
                        color: white;
                        border-radius: 10px;
                        padding: 10px 20px;
                        font-size: 14px;
                        font-weight: bold;
                    }
                """)
                self.upload_progress_label.setAlignment(Qt.AlignCenter)
                # 确保标签有正确的父窗口
                self.upload_progress_label.setParent(self)
                # 设置标签为不接受鼠标事件，避免阻塞用户交互
                self.upload_progress_label.setAttribute(Qt.WA_TransparentForMouseEvents, True)
            else:
                # 确保标签的父窗口正确
                if self.upload_progress_label.parent() != self:
                    self.upload_progress_label.setParent(self)
            
            # 设置消息
            self.upload_progress_label.setText(message)
            
            # 调整位置（居中显示）
            self.upload_progress_label.adjustSize()
            x = (self.width() - self.upload_progress_label.width()) // 2
            y = (self.height() - self.upload_progress_label.height()) // 2
            self.upload_progress_label.move(x, y)
            
            # 显示标签
            self.upload_progress_label.show()
            self.upload_progress_label.raise_()
            
            # 如果指定了持续时间，则自动隐藏
            if duration > 0:
                QTimer.singleShot(duration, self.hide_upload_progress)
                
        except Exception as e:
            print(f"显示上传进度时出错: {e}")
            import traceback
            traceback.print_exc()
    
    def hide_upload_progress(self):
        """隐藏上传进度提示"""
        try:
            if self.upload_progress_label:
                self.upload_progress_label.hide()
                print("已隐藏上传进度提示")
            else:
                print("upload_progress_label 为 None，无法隐藏")
        except Exception as e:
            print(f"隐藏上传进度时出错: {e}")
            import traceback
            traceback.print_exc()
    
    def init_database(self):
        """初始化SQLite数据库"""
        try:
            # 使用传入的用户名或默认用户名
            username = self.username
            
            # 确定数据库文件路径，使用用户名作为前缀
            # 对于打包后的应用，使用应用程序所在目录
            if getattr(sys, 'frozen', False):
                # 如果是打包的应用程序，使用应用程序所在目录
                app_dir = os.path.dirname(sys.executable)
                self.db_file = os.path.join(app_dir, f"{username}_todo_data.db")
                print(f"打包环境，使用数据库路径: {self.db_file}")
            else:
                # 开发环境，使用当前目录
                self.db_file = f"{username}_todo_data.db"
                print(f"开发环境，使用数据库路径: {self.db_file}")
            
            # 如果不存在数据库文件，则创建数据库和表
            conn = sqlite3.connect(self.db_file)
            cursor = conn.cursor()
            
            # 创建全局版本号表
            cursor.execute('''
            CREATE TABLE IF NOT EXISTS database_version (
                id INTEGER PRIMARY KEY CHECK (id = 1),
                version INTEGER NOT NULL DEFAULT 1,
                last_updated TEXT NOT NULL
            )
            ''')
            
            # 初始化全局版本号（如果不存在）
            cursor.execute("SELECT COUNT(*) FROM database_version WHERE id = 1")
            if cursor.fetchone()[0] == 0:
                current_time = QDateTime.currentDateTime().toString("yyyy-MM-dd HH:mm:ss")
                cursor.execute(
                    "INSERT INTO database_version (id, version, last_updated) VALUES (1, 1, ?)",
                    (current_time,)
                )
            
            # 创建待办事项表，包含important字段、completion_datetime字段和due_date字段
            cursor.execute('''
            CREATE TABLE IF NOT EXISTS todos (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                content TEXT NOT NULL,
                datetime TEXT NOT NULL,
                completed INTEGER NOT NULL DEFAULT 0,
                important INTEGER NOT NULL DEFAULT 0,
                reminder_time TEXT,
                due_date TEXT
            )
            ''')
            
            # 检查是否需要迁移数据（添加datetime列）
            cursor.execute("PRAGMA table_info(todos)")
            columns = [column[1] for column in cursor.fetchall()]
            if 'datetime' not in columns:
                # 为现有表添加datetime列
                try:
                    cursor.execute("ALTER TABLE todos ADD COLUMN datetime TEXT DEFAULT '2023-01-01 00:00'")
                    print("成功添加datetime列")
                except sqlite3.OperationalError:
                    print("datetime列已存在")
            
            # 检查是否需要添加important列
            if 'important' not in columns:
                try:
                    cursor.execute("ALTER TABLE todos ADD COLUMN important INTEGER DEFAULT 0")
                    print("成功添加important列")
                except sqlite3.OperationalError:
                    print("important列已存在")
            
            # 检查是否需要添加reminder_time列
            if 'reminder_time' not in columns:
                try:
                    cursor.execute("ALTER TABLE todos ADD COLUMN reminder_time TEXT")
                    print("成功添加reminder_time列")
                except sqlite3.OperationalError:
                    print("reminder_time列已存在")
            
            # 检查是否需要添加due_date列
            if 'due_date' not in columns:
                try:
                    cursor.execute("ALTER TABLE todos ADD COLUMN due_date TEXT")
                    print("成功添加due_date列")
                except sqlite3.OperationalError:
                    print("due_date列已存在")
            
            # 检查是否需要移除version列（从旧版本迁移）
            if 'version' in columns:
                # 创建临时表
                cursor.execute('''
                CREATE TABLE todos_new (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    content TEXT NOT NULL,
                    datetime TEXT NOT NULL,
                    completed INTEGER NOT NULL DEFAULT 0,
                    important INTEGER NOT NULL DEFAULT 0,
                    completion_datetime TEXT,
                    due_date TEXT
                )
                ''')
                
                # 复制数据到新表
                cursor.execute("INSERT INTO todos_new (id, content, datetime, completed, important, reminder_time) SELECT id, content, datetime, completed, important, completion_datetime FROM todos")
                
                # 删除旧表
                cursor.execute("DROP TABLE todos")
                
                # 重命名新表
                cursor.execute("ALTER TABLE todos_new RENAME TO todos")
                print("成功移除version列，迁移到全局版本号")
            
            conn.commit()
            conn.close()
            print("数据库初始化成功")
            
        except sqlite3.Error as e:
            error_msg = f"初始化数据库错误: {str(e)}"
            print(error_msg)
            # 创建一个简单的错误对话框，因为主窗口可能尚未初始化
            msg_box = QMessageBox()
            msg_box.setIcon(QMessageBox.Critical)
            msg_box.setWindowTitle("数据库错误")
            msg_box.setText(error_msg)
            msg_box.exec_()
            
        except Exception as e:
            error_msg = f"初始化过程中发生未知错误: {str(e)}"
            print(error_msg)
            # 创建一个简单的错误对话框，因为主窗口可能尚未初始化
            msg_box = QMessageBox()
            msg_box.setIcon(QMessageBox.Critical)
            msg_box.setWindowTitle("初始化错误")
            msg_box.setText(error_msg)
            msg_box.exec_()
    
    def initUI(self):
        # 设置窗口标题和初始大小
        self.setWindowTitle(f'待办事项竖版 {APP_VERSION}')  # 添加"竖版"标识
        self.setGeometry(100, 100, 600, 650)  # 减小默认窗口宽度，保持高度
        self.setMinimumSize(300, 400)  # 设置最小宽度为300像素
        
        # 创建中央窗口部件
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        
        # 创建主布局 - 使用水平分割布局
        main_layout = QHBoxLayout(central_widget)
        main_layout.setContentsMargins(0, 0, 0, 0)
        main_layout.setSpacing(0)
        
        # 创建左侧按钮区域
        left_sidebar = QWidget()
        left_sidebar.setFixedWidth(70)  # 设置固定宽度
        left_sidebar.setObjectName("leftSidebar")
        left_sidebar_layout = QVBoxLayout(left_sidebar)
        left_sidebar_layout.setContentsMargins(5, 10, 5, 10)
        left_sidebar_layout.setSpacing(8)
        
        # 为按钮区域添加左侧、上侧、下侧边框
        left_sidebar.setStyleSheet(f"""
            QWidget#leftSidebar {{
                background-color: {COLORS['card_bg']};
                border-left: 2px solid {COLORS['border']};
                border-top: 2px solid {COLORS['border']};
                border-bottom: 2px solid {COLORS['border']};
                border-right: none;
            }}
        """)
        
        # 创建右侧内容区域
        right_content = QWidget()
        right_content_layout = QVBoxLayout(right_content)
        right_content_layout.setContentsMargins(20, 20, 20, 20)
        right_content_layout.setSpacing(15)
        
        # 创建顶部工具栏（包含关闭按钮）
        top_bar = QWidget()
        top_bar_layout = QHBoxLayout(top_bar)
        top_bar_layout.setContentsMargins(0, 0, 0, 0)
        
        # 创建标题和版本号容器，使用水平布局
        header_container = QWidget()
        header_layout = QHBoxLayout(header_container)
        header_layout.setContentsMargins(0, 0, 0, 0)
        header_layout.setSpacing(5)
        
        # 添加标题
        title_label = QLabel("待办事项管理")
        title_label.setFont(QFont("Microsoft YaHei", 15, QFont.Bold))
        title_label.setStyleSheet("color: #4895ef;")
        
        # 添加"竖版"标识，红色字体
        vertical_label = QLabel("竖版")
        vertical_label.setFont(QFont("Microsoft YaHei", 8))
        vertical_label.setStyleSheet("color: #ff4757; font-weight: bold;")  # 红色字体，加粗
        vertical_label.setAlignment(Qt.AlignBottom)
        
        # 创建版本号容器，包含版本号和更新提示
        self.version_container = QWidget()
        version_container_layout = QHBoxLayout(self.version_container)
        version_container_layout.setContentsMargins(0, 0, 0, 0)
        version_container_layout.setSpacing(3)
        
        # 添加版本号标签
        self.version_label = QLabel(APP_VERSION)
        self.version_label.setFont(QFont("Microsoft YaHei", 8))
        self.version_label.setStyleSheet("color: #6c757d; padding: 2px;")
        self.version_label.setAlignment(Qt.AlignBottom)
        # 启用鼠标跟踪，使版本号可点击
        self.version_label.setMouseTracking(True)
        # 设置鼠标指针样式为手型
        self.version_label.setCursor(Qt.PointingHandCursor)
        # 连接点击事件 - 使用更安全的方式
        def on_version_clicked(event):
            if hasattr(self, 'check_for_updates'):
                self.check_for_updates()
            else:
                QMessageBox.information(self, "提示", "检查更新功能正在初始化，请稍后再试。")

        self.version_label.mousePressEvent = on_version_clicked
        
        # 添加更新提示标签（初始隐藏）
        self.update_hint_label = QLabel("有更新")
        self.update_hint_label.setFont(QFont("Microsoft YaHei", 8))
        self.update_hint_label.setStyleSheet("""
            QLabel {
                color: white;
                background-color: #28a745;
                padding: 1px 4px;
                border-radius: 3px;
                font-weight: bold;
            }
            QLabel:hover {
                background-color: #218838;
            }
        """)
        self.update_hint_label.setAlignment(Qt.AlignCenter)
        self.update_hint_label.setVisible(False)  # 初始隐藏
        # 启用鼠标跟踪，使更新提示可点击
        self.update_hint_label.setMouseTracking(True)
        # 设置鼠标指针样式为手型
        self.update_hint_label.setCursor(Qt.PointingHandCursor)
        # 连接点击事件 - 与版本号点击事件相同
        self.update_hint_label.mousePressEvent = on_version_clicked
        
        # 将版本号和更新提示添加到容器
        version_container_layout.addWidget(self.version_label)
        version_container_layout.addWidget(self.update_hint_label)
        version_container_layout.addStretch()
        
        # 将标题、"竖版"标识和版本号容器添加到同一水平布局
        header_layout.addWidget(title_label)
        header_layout.addWidget(vertical_label, 0, Qt.AlignBottom)
        header_layout.addWidget(self.version_container, 0, Qt.AlignBottom)
        header_layout.addStretch(1)
        
        # 在右侧添加关闭按钮
        close_button = QPushButton("×")
        close_button.setFixedSize(30, 30)
        close_button.setStyleSheet(f"""
            QPushButton {{
                border-radius: 15px;
                font-family: Arial;
                font-size: 20px;
                font-weight: bold;
                color: {COLORS['text_secondary']};
                background-color: transparent;
            }}
            QPushButton:hover {{
                color: {COLORS['error']};
            }}
        """)
        close_button.clicked.connect(self.closeAndQuit)
        
        # 添加组件到顶部工具栏
        top_bar_layout.addWidget(header_container)
        top_bar_layout.addStretch(1)
        top_bar_layout.addWidget(close_button)
        
        # 添加顶部工具栏到右侧内容区域
        right_content_layout.addWidget(top_bar)
        
        # 创建输入框区域（只包含输入框）
        input_container = QWidget()
        input_container.setObjectName("inputContainer")
        input_layout = QVBoxLayout(input_container)
        input_layout.setContentsMargins(15, 15, 15, 15)
        input_layout.setSpacing(10)
        
        # 创建输入框
        self.todo_input = QLineEdit()
        self.todo_input.setPlaceholderText('输入新的待办事项...')
        self.todo_input.returnPressed.connect(self.addTodo)
        self.todo_input.setMinimumHeight(40)
        
        # 设置输入框样式
        input_style = f"""
            QLineEdit {{
                border-radius: 8px;
                font-size: 13px;
                padding: 10px 15px;
                background-color: {COLORS['background']};
                color: {COLORS['text_primary']};
                border: 2px solid {COLORS['border']};
            }}
            QLineEdit:focus {{
                border-color: {COLORS['primary']};
                background-color: {COLORS['background']};
            }}
            QLineEdit::placeholder {{
                color: {COLORS['text_secondary']};
            }}
        """
        self.todo_input.setStyleSheet(input_style)
        
        # 添加输入框到输入容器
        input_layout.addWidget(self.todo_input)
        
        # 添加输入容器到右侧内容区域
        right_content_layout.addWidget(input_container)
        
        # 创建左侧边栏按钮
        # AI按钮
        self.ai_button = QPushButton()
        ai_icon = get_embedded_svg_icon('ai')
        if ai_icon:
            self.ai_button.setIcon(ai_icon)
            self.ai_button.setIconSize(QSize(24, 24))
        else:
            self.ai_button.setText('🤖')  # Fallback to emoji if SVG not found
        self.ai_button.enterEvent = lambda event: self.show_custom_tooltip(event, self.ai_button, 'AI')
        self.ai_button.leaveEvent = lambda event: self.hide_custom_tooltip()
        self.ai_button.clicked.connect(self.launchAIPlugin)
        self.ai_button.setFixedSize(45, 45)
        self.ai_button.setStyleSheet(f"""
            QPushButton {{
                border-radius: 10px;
                font-size: 20px;
                background-color: {COLORS['card_bg']};
                color: {COLORS['text_primary']};
                border: 2px solid {COLORS['border']};
            }}
            QPushButton:hover {{
                background-color: {COLORS['accent']};
                color: white;
                border-color: {COLORS['accent']};
            }}
            QPushButton:pressed {{
                background-color: {COLORS['primary']};
                color: white;
            }}
        """)
        
        # 锁定按钮
        self.lock_button = QPushButton()
        lock_icon = get_embedded_svg_icon('lock')
        if lock_icon:
            self.lock_button.setIcon(lock_icon)
            self.lock_button.setIconSize(QSize(24, 24))
        else:
            self.lock_button.setText('🔒')  # Fallback to emoji if SVG not found
        self.lock_button.enterEvent = lambda event: self.show_custom_tooltip(event, self.lock_button, '锁定到桌面')
        self.lock_button.leaveEvent = lambda event: self.hide_custom_tooltip()
        self.lock_button.setCheckable(True)
        self.lock_button.clicked.connect(self.toggleLock)
        self.lock_button.setFixedSize(45, 45)
        self.lock_button.setStyleSheet(f"""
            QPushButton {{
                border-radius: 10px;
                font-size: 20px;
                background-color: {COLORS['card_bg']};
                color: {COLORS['text_primary']};
                border: 2px solid {COLORS['border']};
            }}
            QPushButton:checked {{
                background-color: {COLORS['primary']};
                color: white;
                border-color: {COLORS['primary']};
            }}
            QPushButton:hover {{
                background-color: {COLORS['accent']};
                color: white;
                border-color: {COLORS['accent']};
            }}
            QPushButton:checked:hover {{
                background-color: {COLORS['primary']};
                color: white;
                border-color: {COLORS['primary']};
            }}
        """)
        
        # 自启动按钮
        self.autostart_button = QPushButton()
        autostart_icon = get_embedded_svg_icon('autostart')
        if autostart_icon:
            self.autostart_button.setIcon(autostart_icon)
            self.autostart_button.setIconSize(QSize(24, 24))
        else:
            self.autostart_button.setText('🚀')  # Fallback to emoji if SVG not found
        self.autostart_button.enterEvent = lambda event: self.show_custom_tooltip(event, self.autostart_button, '自启动')
        self.autostart_button.leaveEvent = lambda event: self.hide_custom_tooltip()
        self.autostart_button.setCheckable(True)
        self.autostart_button.clicked.connect(self.toggleAutostart)
        self.autostart_button.setFixedSize(45, 45)
        self.autostart_button.setChecked(self.isAutostartEnabled())
        self.autostart_button.setStyleSheet(f"""
            QPushButton {{
                border-radius: 10px;
                font-size: 20px;
                background-color: {COLORS['card_bg']};
                color: {COLORS['text_primary']};
                border: 2px solid {COLORS['border']};
            }}
            QPushButton:checked {{
                background-color: {COLORS['primary']};
                color: white;
                border-color: {COLORS['primary']};
            }}
            QPushButton:hover {{
                background-color: {COLORS['accent']};
                color: white;
                border-color: {COLORS['accent']};
            }}
            QPushButton:checked:hover {{
                background-color: {COLORS['primary']};
                color: white;
                border-color: {COLORS['primary']};
            }}
        """)
        
        # 设置按钮
        self.cloud_button = QPushButton()
        settings_icon = get_embedded_svg_icon('settings')
        if settings_icon:
            self.cloud_button.setIcon(settings_icon)
            self.cloud_button.setIconSize(QSize(24, 24))
        else:
            self.cloud_button.setText('⚙️')  # Fallback to emoji if SVG not found
        self.cloud_button.enterEvent = lambda event: self.show_custom_tooltip(event, self.cloud_button, '设置')
        self.cloud_button.leaveEvent = lambda event: self.hide_custom_tooltip()
        self.cloud_button.clicked.connect(self.open_cloud_settings)
        self.cloud_button.setFixedSize(45, 45)
        self.cloud_button.setStyleSheet(f"""
            QPushButton {{
                border-radius: 10px;
                font-weight: bold;
                font-size: 20px;
                background-color: {COLORS['card_bg']};
                color: {COLORS['text_primary']};
                border: 1px solid {COLORS['border']};
                padding: 5px;
            }}
            QPushButton:hover {{
                background-color: {COLORS['accent']};
                color: white;
                border-color: {COLORS['accent']};
            }}
            QPushButton:pressed {{
                background-color: {COLORS['primary']};
                color: white;
            }}
        """)
        
        # 搜索按钮
        self.search_button = QPushButton()
        search_icon = get_embedded_svg_icon('search')
        if search_icon:
            self.search_button.setIcon(search_icon)
            self.search_button.setIconSize(QSize(24, 24))
        else:
            self.search_button.setText('🔍')  # Fallback to emoji if SVG not found
        self.search_button.enterEvent = lambda event: self.show_custom_tooltip(event, self.search_button, '搜索')
        self.search_button.leaveEvent = lambda event: self.hide_custom_tooltip()
        self.search_button.clicked.connect(lambda: open_search_dialog(self))
        self.search_button.setFixedSize(45, 45)
        
        self.search_button.setStyleSheet(f"""
            QPushButton {{
                border-radius: 10px;
                font-weight: bold;
                font-size: 20px;
                background-color: {COLORS['card_bg']};
                color: {COLORS['text_primary']};
                border: 1px solid {COLORS['border']};
                padding: 5px;
            }}
            QPushButton:hover {{
                background-color: {COLORS['accent']};
                color: white;
                border-color: {COLORS['accent']};
            }}
            QPushButton:pressed {{
                background-color: {COLORS['primary']};
                color: white;
            }}
        """)
        
        # 将按钮添加到左侧边栏，直接与右侧内容区域对齐
        left_sidebar_layout.addSpacing(50)  # 按钮下移100像素
        left_sidebar_layout.addWidget(self.ai_button, 0, Qt.AlignHCenter)
        left_sidebar_layout.addWidget(self.lock_button, 0, Qt.AlignHCenter)
        left_sidebar_layout.addWidget(self.autostart_button, 0, Qt.AlignHCenter)
        left_sidebar_layout.addWidget(self.cloud_button, 0, Qt.AlignHCenter)
        left_sidebar_layout.addWidget(self.search_button, 0, Qt.AlignHCenter)
        left_sidebar_layout.addStretch()  # 添加底部弹性空间
        
        # 创建切换按钮容器
        tab_container = QWidget()
        tab_container.setObjectName("tabContainer")
        tab_layout = QHBoxLayout(tab_container)
        tab_layout.setContentsMargins(0, 0, 0, 0)
        tab_layout.setSpacing(10)
        
        # 创建待办和已完成切换按钮
        self.todo_tab_button = QPushButton('待办事项')
        self.todo_tab_button.setCheckable(True)
        self.todo_tab_button.setChecked(True)  # 默认选中待办事项
        self.todo_tab_button.clicked.connect(lambda checked: self.switchToList(0, checked))
        self.todo_tab_button.setMinimumHeight(40)
        
        self.completed_tab_button = QPushButton('已完成')
        self.completed_tab_button.setCheckable(True)
        self.completed_tab_button.clicked.connect(lambda checked: self.switchToList(1, checked))
        self.completed_tab_button.setMinimumHeight(40)
        
        # 设置按钮样式 - 仔细修改QCheckable按钮的样式
        tab_button_style = f"""
            QPushButton {{
                border-radius: 8px;
                font-weight: bold;
                background-color: {COLORS['card_bg']};
                color: {COLORS['text_primary']};
                border: 1px solid {COLORS['border']};
            }}
            QPushButton:checked {{
                background-color: {COLORS['primary']};
                color: white;
            }}
            QPushButton:hover {{
                background-color: {COLORS['accent']};
                color: white;
            }}
        """
        
        self.todo_tab_button.setStyleSheet(tab_button_style)
        self.completed_tab_button.setStyleSheet(tab_button_style)
        
        # 添加按钮到切换布局
        tab_layout.addWidget(self.todo_tab_button)
        tab_layout.addWidget(self.completed_tab_button)
        
        # 添加切换容器到右侧内容区域
        right_content_layout.addWidget(tab_container)
        
        # 创建单一列表容器
        list_container = QWidget()
        list_container.setObjectName("listContainer")
        list_layout = QVBoxLayout(list_container)
        list_layout.setContentsMargins(15, 15, 15, 15)
        list_layout.setSpacing(10)
        
        # 创建堆叠小部件来管理两个列表
        self.stacked_widget = QStackedWidget()
        
        # 创建两个容器分别放置待办和已完成列表
        todo_container = QWidget()
        todo_layout = QVBoxLayout(todo_container)
        todo_layout.setContentsMargins(0, 0, 0, 0)
        todo_layout.setSpacing(0)
        
        completed_container = QWidget()
        completed_layout = QVBoxLayout(completed_container)
        completed_layout.setContentsMargins(0, 0, 0, 0)
        completed_layout.setSpacing(0)
        
        # 创建待办事项列表
        self.todo_list_widget = QListWidget()
        self.todo_list_widget.setContextMenuPolicy(Qt.CustomContextMenu)
        self.todo_list_widget.customContextMenuRequested.connect(self.showTodoContextMenu)
        self.todo_list_widget.setSpacing(2)
        self.todo_list_widget.itemDoubleClicked.connect(self.showTodoDetail)
        self.todo_list_widget.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.todo_list_widget.setWordWrap(True)
        self.todo_list_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.todo_list_widget.setMinimumWidth(150)  # 减小最小宽度以适应窄窗口
        self.todo_list_widget.setVerticalScrollMode(QListWidget.ScrollPerPixel)
        # 设置选中状态监听器
        self.todo_list_widget.itemSelectionChanged.connect(self.updateTodoSelection)
        
        # 创建已完成事项列表
        self.completed_list_widget = QListWidget()
        self.completed_list_widget.setContextMenuPolicy(Qt.CustomContextMenu)
        self.completed_list_widget.customContextMenuRequested.connect(self.showCompletedContextMenu)
        self.completed_list_widget.setSpacing(2)
        self.completed_list_widget.itemDoubleClicked.connect(self.showCompletedDetail)
        self.completed_list_widget.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.completed_list_widget.setWordWrap(True)
        self.completed_list_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.completed_list_widget.setMinimumWidth(150)  # 减小最小宽度以适应窄窗口
        self.completed_list_widget.setVerticalScrollMode(QListWidget.ScrollPerPixel)
        # 设置选中状态监听器
        self.completed_list_widget.itemSelectionChanged.connect(self.updateCompletedSelection)
        
        # 将列表添加到对应容器
        todo_layout.addWidget(self.todo_list_widget)
        completed_layout.addWidget(self.completed_list_widget)
        
        # 将容器添加到堆叠小部件
        self.stacked_widget.addWidget(todo_container)
        self.stacked_widget.addWidget(completed_container)
        
        # 默认显示待办列表
        self.stacked_widget.setCurrentIndex(0)
        
        # 将堆叠小部件添加到列表容器
        list_layout.addWidget(self.stacked_widget)
        
        # 添加列表容器到右侧内容区域，使用较大的比例
        right_content_layout.addWidget(list_container, 100)
        
        # 添加底部版权信息，无间距
        copyright_label = QLabel(COPYRIGHT_INFO)
        copyright_label.setFont(QFont("Microsoft YaHei", 8))
        copyright_label.setStyleSheet("color: #666666; margin: 0;")  # 深灰色
        copyright_label.setAlignment(Qt.AlignCenter)
        right_content_layout.addWidget(copyright_label)
        
        # 将左侧边栏和右侧内容添加到主布局
        main_layout.addWidget(left_sidebar)
        main_layout.addWidget(right_content, 1)  # 右侧内容占据剩余空间
        
        # 设置应用窗口和控件样式
        self.setStyleSheet(f"""
            QMainWindow {{
                background-color: {COLORS['background']};
                border-radius: 12px;  /* 减小圆角以适应窄窗口 */
                border: 2px solid {COLORS['border']};
                box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15), 0 4px 16px rgba(0, 0, 0, 0.1);
            }}
            QWidget#centralWidget {{
                background-color: {COLORS['background']};
                border-radius: 12px;  /* 减小圆角以适应窄窗口 */
                border: 2px solid {COLORS['border']};
                box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15), 0 4px 16px rgba(0, 0, 0, 0.1);
            }}
            /* 修复圆角区域的黑色问题 */
            QMainWindow::corner {{
                background-color: transparent;
            }}
            QWidget#centralWidget::corner {{
                background-color: transparent;
            }}
            /* 确保所有圆角区域都是透明的 */
            QMainWindow {{
                background-color: {COLORS['background']};
                border-radius: 12px;
                border: 2px solid {COLORS['border']};
                box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15), 0 4px 16px rgba(0, 0, 0, 0.1);
            }}
            QWidget#centralWidget {{
                background-color: {COLORS['background']};
                border-radius: 12px;
                border: 2px solid {COLORS['border']};
                box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15), 0 4px 16px rgba(0, 0, 0, 0.1);
            }}
            QWidget#controlContainer, QWidget#listContainer {{
                background-color: {COLORS['card_bg']};
                border-radius: 8px;  /* 减小圆角以适应窄窗口 */
                border: 1px solid {COLORS['border']};
                box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08), 0 2px 8px rgba(0, 0, 0, 0.06);
            }}
            #leftSidebar {{
                background-color: {COLORS['card_bg']};
                border-right: 1px solid {COLORS['border']};
            }}
            QListWidget {{
                background-color: transparent;
                border: none;
                font-size: 13px;  /* 稍微减小字体大小 */
                font-family: 'Microsoft YaHei';
                font-weight: bold;
                outline: none;
            }}
            QListWidget::item {{
                padding: 4px;  /* 减小内边距 */
                border: none;
                background-color: transparent;  /* 确保列表项背景透明，以显示自定义widget的背景 */
            }}
            QListWidget::item:selected {{
                background-color: rgba(67, 97, 238, 0.25);
                border-radius: 4px;
            }}
            QListWidget::item:hover {{
                background-color: rgba(67, 97, 238, 0.1);
                border-radius: 4px;
            }}
            QListWidget::item:pressed {{
                background-color: rgba(67, 97, 238, 0.3);
                border-radius: 4px;
            }}
            QLineEdit {{
                height: 40px;
                border: 1px solid {COLORS['border']};
                border-radius: 8px;
                padding: 2px 15px;
                font-size: 14px;
                font-family: 'Microsoft YaHei';
                background-color: white;
                selection-background-color: {COLORS['primary']};
                box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
            }}
            QLineEdit:focus {{
                border: 2px solid {COLORS['primary']};
                box-shadow: 0 4px 16px rgba(67, 97, 238, 0.15), 0 2px 8px rgba(67, 97, 238, 0.1);
            }}
            QPushButton {{
                height: 40px;
                border: none;
                border-radius: 8px;
                font-size: 14px;
                font-family: 'Microsoft YaHei';
                font-weight: bold;
                background-color: {COLORS['primary']};
                color: white;
            }}
            QPushButton:hover {{
                background-color: {COLORS['accent']};
            }}
            QPushButton:pressed {{
                background-color: {COLORS['secondary']};
            }}
            QPushButton:checked {{
                background-color: {COLORS['success']};
            }}
            QLabel {{
                background-color: transparent;
                font-family: 'Microsoft YaHei';
                font-weight: bold;
            }}
            QScrollBar:vertical {{
                border: none;
                background: {COLORS['card_bg']};
                width: 6px;  /* 减小滚动条宽度 */
                border-radius: 3px;
                margin: 0px;
            }}
            QScrollBar::handle:vertical {{
                background: {COLORS['border']};
                min-height: 20px;
                border-radius: 3px;
            }}
            QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {{
                height: 0px;
            }}
            QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {{
                background: none;
            }}
            QToolTip {{
                background-color: #333333;
                color: white;
                border: 1px solid #555555;
                border-radius: 4px;
                padding: 4px 8px;
                font-size: 12px;
                font-family: 'Microsoft YaHei';
            }}
        """)
        
        # 设置阴影效果
        for container in [input_container, list_container]:
            if container:
                shadow = QGraphicsDropShadowEffect(container)
                shadow.setBlurRadius(15)
                shadow.setColor(QColor(0, 0, 0, 30))
                shadow.setOffset(0, 3)
                container.setGraphicsEffect(shadow)
        
        # 为主窗口添加阴影效果
        main_window_shadow = QGraphicsDropShadowEffect(self)
        main_window_shadow.setBlurRadius(25)
        main_window_shadow.setColor(QColor(0, 0, 0, 60))
        main_window_shadow.setOffset(0, 8)
        self.setGraphicsEffect(main_window_shadow)
        
        # 为顶部工具栏添加阴影效果
        top_bar_shadow = QGraphicsDropShadowEffect(top_bar)
        top_bar_shadow.setBlurRadius(20)
        top_bar_shadow.setColor(QColor(0, 0, 0, 40))
        top_bar_shadow.setOffset(0, 4)
        top_bar.setGraphicsEffect(top_bar_shadow)
        
        # 为切换按钮容器添加阴影效果
        tab_shadow = QGraphicsDropShadowEffect(tab_container)
        tab_shadow.setBlurRadius(18)
        tab_shadow.setColor(QColor(0, 0, 0, 35))
        tab_shadow.setOffset(0, 3)
        tab_container.setGraphicsEffect(tab_shadow)
        
        # 优化现有容器的阴影效果
        for container in [input_container, list_container]:
            if container:
                # 移除旧的阴影效果
                container.setGraphicsEffect(None)
                # 应用新的增强阴影效果
                shadow = QGraphicsDropShadowEffect(container)
                shadow.setBlurRadius(20)
                shadow.setColor(QColor(0, 0, 0, 45))
                shadow.setOffset(0, 5)
                container.setGraphicsEffect(shadow)
        
        # 设置中央窗口部件的对象名，以便可以在样式表中引用
        central_widget.setObjectName("centralWidget")
        
        # 设置窗口属性 - 允许调整大小
        self.setWindowFlags(Qt.FramelessWindowHint)  # 无边框窗口，图标显示在任务栏
        
        # 确保窗口在任务栏中显示
        self.setWindowTitle(f'待办事项竖版 {APP_VERSION}')
        
        # 创建圆角掩码，确保圆角区域透明
        self.createRoundedMask()
        
        # 添加阴影效果
        self.add_shadow()
    
    def createRoundedMask(self):
        """创建圆角掩码，确保圆角区域透明"""
        # 创建圆角路径
        path = QPainterPath()
        rect = self.rect()
        radius = 12  # 与CSS中的border-radius保持一致
        
        # 绘制圆角矩形路径 - 将QRect转换为QRectF
        path.addRoundedRect(QRectF(rect), radius, radius)
        
        # 创建掩码
        mask = QRegion(path.toFillPolygon().toPolygon())
        self.setMask(mask)
    
    def add_shadow(self):
        """添加阴影效果"""
        # 添加阴影到整个窗口
        self.effect_shadow = QGraphicsDropShadowEffect(self)
        self.effect_shadow.setOffset(2, 2)  # 偏移
        self.effect_shadow.setBlurRadius(15)  # 阴影半径
        self.effect_shadow.setColor(QColor(0, 0, 0, 80))  # 阴影颜色，带透明度
        self.setGraphicsEffect(self.effect_shadow)  # 将设置套用到整个窗口
    
    def setQuickTime(self, days):
        """设置快速时间"""
        if days == 0:  # 今天
            self.due_date_edit.setDateTime(QDateTime.currentDateTime())
        elif days == 1:  # 明天
            self.due_date_edit.setDateTime(QDateTime.currentDateTime().addDays(1))
        elif days == 2:  # 后天
            self.due_date_edit.setDateTime(QDateTime.currentDateTime().addDays(2))
        elif days == 7:  # 一周后
            self.due_date_edit.setDateTime(QDateTime.currentDateTime().addDays(7))
    
    def checkDueItems(self):
        """检查到期事项并显示提醒"""
        print("=== 开始检查到期事项 ===")
        try:
            conn = sqlite3.connect(self.db_file)
            cursor = conn.cursor()
            
            # 获取未完成且有提醒时间的事项
            cursor.execute("""
                SELECT content, due_date FROM todos 
                WHERE completed = 0 AND due_date IS NOT NULL AND due_date != ''
            """)
            
            current_time = QDateTime.currentDateTime()
            overdue_items = []
            due_now_items = []
            
            print(f"检查到期事项 - 当前时间: {current_time.toString('yyyy-MM-dd HH:mm:ss')}")
            
            for content, due_date in cursor.fetchall():
                if due_date:
                    due_time = QDateTime.fromString(due_date, "yyyy-MM-dd HH:mm")
                    if due_time.isValid():
                        # 计算时间差（秒）
                        time_diff_seconds = current_time.secsTo(due_time)
                        print(f"检查事项: {content[:20]}... 截止时间: {due_time.toString('yyyy-MM-dd HH:mm:ss')} 时间差: {time_diff_seconds}秒")
                        
                        if time_diff_seconds <= 0:
                            # 已过期，计算超时时间
                            overdue_seconds = abs(time_diff_seconds)
                            hours = overdue_seconds // 3600
                            minutes = (overdue_seconds % 3600) // 60
                            overdue_items.append(f"{content} (超时：{hours}小时{minutes}分)")
                            print(f"已过期: {content[:20]}... (超时{hours}小时{minutes}分)")
                        elif time_diff_seconds <= 300:  # 5分钟内到期
                            # 即将到期
                            due_now_items.append(content)
                            print(f"即将到期: {content[:20]}... (还有{time_diff_seconds}秒)")
            
            conn.close()
            
            print(f"检查结果 - 即将到期: {len(due_now_items)}项, 已过期: {len(overdue_items)}项")
            
            # 显示提醒 - 提醒即将到期和已超时的事项
            if due_now_items:
                # 过滤掉已经提醒过的事项
                new_due_items = [item for item in due_now_items if item not in self.reminded_items]
                if new_due_items:
                    print("触发到期提醒!")
                    self.showReminder("到期提醒", f"以下事项即将到期：\n" + "\n".join(new_due_items))
                    # 记录已提醒的事项
                    self.reminded_items.update(new_due_items)
            
            # 提醒已超时事项
            if overdue_items:
                # 过滤掉已经提醒过的事项
                new_overdue_items = [item for item in overdue_items if item not in self.reminded_items]
                if new_overdue_items:
                    print("触发超时提醒!")
                    self.showReminder("超时提醒", f"以下事项已超时：\n" + "\n".join(new_overdue_items))
                    # 记录已提醒的事项
                    self.reminded_items.update(new_overdue_items)
                
        except Exception as e:
            print(f"检查到期事项时出错: {e}")
    
    def refreshTimeDisplay(self):
        """刷新界面上的时间显示，无感知更新"""
        try:
            # 只刷新待办列表中的时间显示，不重新加载整个列表
            self.refreshTodoListTimeDisplay()
        except Exception as e:
            print(f"刷新时间显示时出错: {e}")
    
    def refreshTodoListTimeDisplay(self):
        """刷新待办列表中的时间显示"""
        try:
            # 获取当前待办列表中的所有widget
            updated_count = 0
            for i in range(self.todo_list_widget.count()):
                item = self.todo_list_widget.item(i)
                if item:
                    widget = self.todo_list_widget.itemWidget(item)
                    if widget and hasattr(widget, 'updateTimeDisplay'):
                        widget.updateTimeDisplay()
                        updated_count += 1
            
            # 每10次刷新输出一次调试信息
            if hasattr(self, '_refresh_count'):
                self._refresh_count += 1
            else:
                self._refresh_count = 1
                
            if self._refresh_count % 6 == 0:  # 每60秒输出一次（10秒*6）
                print(f"时间显示刷新完成，更新了 {updated_count} 个待办项")
        except Exception as e:
            print(f"刷新待办列表时间显示时出错: {e}")
    
    def showReminder(self, title, message):
        """显示提醒对话框"""
        # 播放提醒提示音
        self.play_reminder_sound()
        
        msg_box = QMessageBox(self)
        msg_box.setWindowTitle(title)
        msg_box.setText(message)
        msg_box.setIcon(QMessageBox.Warning)
        msg_box.setStandardButtons(QMessageBox.Ok)
        
        # 连接确定按钮的点击事件，停止播放声音
        ok_button = msg_box.button(QMessageBox.Ok)
        ok_button.clicked.connect(self.stop_reminder_sound)
        
        msg_box.exec_()
    
    def play_reminder_sound(self):
        """播放提醒提示音"""
        try:
            import os
            import sys
            # 确保sound.mp3文件在程序同目录下
            if getattr(sys, 'frozen', False):
                # 如果是打包后的exe文件
                current_dir = os.path.dirname(sys.executable)
            else:
                # 如果是Python脚本
                current_dir = os.path.dirname(os.path.abspath(__file__))
            sound_file = os.path.join(current_dir, "sound.mp3")
            if os.path.exists(sound_file):
                try:
                    # 尝试使用pygame播放mp3文件
                    import pygame
                    if not pygame.mixer.get_init():
                        pygame.mixer.init()
                    
                    pygame.mixer.music.load(sound_file)
                    pygame.mixer.music.play()
                except ImportError:
                    # 如果没有安装pygame，使用winsound播放默认提示音
                    import winsound
                    winsound.MessageBeep(winsound.MB_ICONASTERISK)
                except Exception as e:
                    # 其他错误也使用默认提示音
                    import winsound
                    winsound.MessageBeep(winsound.MB_ICONASTERISK)
            else:
                # 如果没有sound.mp3文件，使用默认提示音
                import winsound
                winsound.MessageBeep(winsound.MB_ICONASTERISK)
        except Exception as e:
            # 如果播放提示音失败，静默处理
            print(f"播放提示音失败: {e}")
            # 失败时使用默认系统提示音
            try:
                import winsound
                winsound.MessageBeep(winsound.MB_ICONASTERISK)
            except:
                pass
    
    def stop_reminder_sound(self):
        """停止播放提醒提示音"""
        try:
            import pygame
            if pygame.mixer.get_init():
                pygame.mixer.music.stop()
        except ImportError:
            # 如果没有安装pygame，无法停止winsound播放的声音
            pass
        except Exception as e:
            print(f"停止播放提示音失败: {e}")
    
    def toggleSortMode(self):
        """切换排序模式"""
        self.sort_by_due_date = not self.sort_by_due_date
        if self.sort_by_due_date:
            self.sort_button.setText('按截止')
            self.sort_button.setStyleSheet(f"""
                QPushButton {{
                    border-radius: 8px;
                    font-weight: bold;
                    font-size: 12px;
                    background-color: {COLORS['primary']};
                    color: white;
                    border: 1px solid {COLORS['primary']};
                    padding: 8px 12px;
                }}
                QPushButton:hover {{
                    background-color: {COLORS['accent']};
                    color: white;
                    border-color: {COLORS['accent']};
                }}
            """)
        else:
            self.sort_button.setText('按添加')
            self.sort_button.setStyleSheet(f"""
                QPushButton {{
                    border-radius: 8px;
                    font-weight: bold;
                    font-size: 12px;
                    background-color: {COLORS['card_bg']};
                    color: {COLORS['text_primary']};
                    border: 1px solid {COLORS['border']};
                    padding: 8px 12px;
                }}
                QPushButton:hover {{
                    background-color: {COLORS['accent']};
                    color: white;
                    border-color: {COLORS['accent']};
                }}
            """)
        
        # 重新加载数据以应用新的排序
        self.loadData()
    
    def addTodo(self):
        """添加待办事项，弹出完成时间选择对话框"""        
        text = self.todo_input.text().strip()
        if text:
            # 弹出完成时间选择对话框
            dialog = CompletionTimeDialog(self)
            if dialog.exec() == QDialog.Accepted:
                # 检查是否是取消但不设置时间的情况
                if dialog.cancel_without_time_flag:
                    # 取消但不设置时间，due_date为None
                    due_date = None
                else:
                    # 正常确定，获取选择的完成时间
                    due_date = dialog.datetime_edit.dateTime().toString("yyyy-MM-dd HH:mm") if dialog.datetime_edit.dateTime().isValid() else None
                
                # 直接使用当前日期时间
                current_datetime = QDateTime.currentDateTime().toString("yyyy-MM-dd HH:mm")
                
                # 添加到数据库
                conn = sqlite3.connect(self.db_file)
                cursor = conn.cursor()
                
                # 添加待办事项
                cursor.execute(
                    "INSERT INTO todos (content, datetime, completed, important, due_date) VALUES (?, ?, 0, 0, ?)",
                    (text, current_datetime, due_date)
                )
                
                # 更新全局版本号
                current_time = QDateTime.currentDateTime().toString("yyyy-MM-dd HH:mm:ss")
                cursor.execute(
                    "UPDATE database_version SET version = version + 1, last_updated = ? WHERE id = 1",
                    (current_time,)
                )
                
                conn.commit()
                conn.close()
                
                # 刷新列表（新添加的事项会按时间排序显示）
                self.loadData()
                self.todo_input.clear()
                
                print(f"已添加待办事项: '{text}', 时间: {current_datetime}, 完成时间: {due_date}")
                
                # 自动上传到远程服务器
                if self.auto_upload_enabled:
                    self.auto_upload_to_remote()
    
    def updateTodoList(self):
        """更新待办事项列表显示"""
        self.todo_list_widget.clear()
        print(f"更新待办列表，共 {len(self.todo_list)} 项")
        
        for idx, (content, datetime_str, is_important, due_date) in enumerate(self.todo_list):
            print(f"显示第 {idx} 项: {content}, {datetime_str}")
            
            # 创建列表项和自定义小部件
            item = QListWidgetItem()
            item_widget = TodoItemWidget(content, datetime_str, is_completed=False, is_important=is_important, due_date=due_date)
            
            # 先添加到列表中
            self.todo_list_widget.addItem(item)
            self.todo_list_widget.setItemWidget(item, item_widget)
            
            # 获取视口宽度，为列表项设置合适的宽度
            viewport_width = self.todo_list_widget.viewport().width()
            
            # 设置列表项大小 - 使用item_widget的sizeHint来确定合适的高度
            size_hint = item_widget.sizeHint()
            safe_width = max(200, viewport_width - 15)  # 保证最小宽度为200px
            
            # 根据高度设置动态调整高度
            item.setSizeHint(QSize(safe_width, size_hint.height()))
            
            # 设置列表项背景透明
            item.setBackground(QColor(0, 0, 0, 0))
    
    def updateCompletedList(self):
        """更新已完成事项列表显示"""
        self.completed_list_widget.clear()
        print(f"更新已完成列表，共 {len(self.completed_list)} 项")
        
        for idx, (content, datetime_str, is_important, completion_datetime, due_date) in enumerate(self.completed_list):
            print(f"显示第 {idx} 项: {content}, {datetime_str}, 完成时间: {completion_datetime}")
            
            # 创建列表项和自定义小部件
            item = QListWidgetItem()
            item_widget = TodoItemWidget(content, datetime_str, is_completed=True, is_important=is_important, reminder_time=completion_datetime, due_date=due_date)
            
            # 先添加到列表中
            self.completed_list_widget.addItem(item)
            self.completed_list_widget.setItemWidget(item, item_widget)
            
            # 获取视口宽度，为列表项设置合适的宽度
            viewport_width = self.completed_list_widget.viewport().width()
            
            # 设置列表项大小 - 使用item_widget的sizeHint来确定合适的高度
            size_hint = item_widget.sizeHint()
            safe_width = max(200, viewport_width - 15)  # 保证最小宽度为200px
            
            # 根据高度设置动态调整高度
            item.setSizeHint(QSize(safe_width, size_hint.height()))
            
            # 设置列表项背景透明
            item.setBackground(QColor(0, 0, 0, 0))
    
    def updateTodoSelection(self):
        """更新待办列表项的选中状态"""
        for i in range(self.todo_list_widget.count()):
            item = self.todo_list_widget.item(i)
            item_widget = self.todo_list_widget.itemWidget(item)
            if isinstance(item_widget, TodoItemWidget):
                is_selected = item.isSelected()
                item_widget.setSelected(is_selected)
    
    def updateCompletedSelection(self):
        """更新已完成列表项的选中状态"""
        for i in range(self.completed_list_widget.count()):
            item = self.completed_list_widget.item(i)
            item_widget = self.completed_list_widget.itemWidget(item)
            if isinstance(item_widget, TodoItemWidget):
                is_selected = item.isSelected()
                item_widget.setSelected(is_selected)
    
    def refreshListsForHeightChange(self):
        """当高度设置改变时刷新列表显示"""
        self.updateTodoList()
        self.updateCompletedList()
    
    def launchAIPlugin(self):
        """启动AI插件程序"""
        try:
            # 检查插件文件是否存在（支持开发环境和打包环境）
            plugin_paths = [
                'ui_plugin_pyqt5.py',  # 开发环境
                'AI插件.exe',           # 打包后的exe文件
                os.path.join(os.path.dirname(sys.executable), 'AI插件.exe'),  # 打包后同目录
                os.path.join(os.path.dirname(__file__), 'AI插件.exe'),  # 相对路径
                os.path.join(os.getcwd(), 'AI插件.exe'),  # 当前工作目录
                os.path.join(os.path.dirname(os.path.abspath(__file__)), 'AI插件.exe'),  # 脚本所在目录
                os.path.join(os.path.dirname(os.path.abspath(sys.executable)), 'AI插件.exe'),  # 可执行文件所在目录
                os.path.join('dist', 'AI插件.exe'),  # dist目录
                os.path.join(os.path.dirname(sys.executable), 'dist', 'AI插件.exe'),  # 可执行文件同级dist目录
            ]
            
            plugin_path = None
            print(f"当前工作目录: {os.getcwd()}")
            print(f"可执行文件路径: {sys.executable}")
            print(f"脚本文件路径: {__file__}")
            print(f"当前工作目录内容: {os.listdir('.')}")
            if os.path.exists('dist'):
                print(f"dist目录内容: {os.listdir('dist')}")
            
            # 遍历路径列表，检查文件是否存在
            for path in plugin_paths:
                print(f"检查路径: {path}")
                if os.path.exists(path):
                    # 验证文件路径，确保不是待办事项管理.exe
                    if '待办事项管理' in path:
                        print(f"跳过错误的文件路径: {path}")
                        continue
                    plugin_path = path
                    print(f"找到AI插件文件: {path}")
                    break
                else:
                    print(f"路径不存在: {path}")
            
            # 如果还是没找到，尝试搜索整个目录
            if not plugin_path:
                print("开始搜索整个目录...")
                search_dirs = [
                    os.getcwd(),
                    os.path.dirname(sys.executable),
                    os.path.dirname(__file__) if __file__ != '<frozen>' else os.getcwd()
                ]
                
                for search_dir in search_dirs:
                    if os.path.exists(search_dir):
                        print(f"搜索目录: {search_dir}")
                        for root, dirs, files in os.walk(search_dir):
                            # 严格检查文件名，确保是AI插件.exe而不是其他exe文件
                            if 'AI插件.exe' in files:
                                candidate_path = os.path.join(root, 'AI插件.exe')
                                # 验证文件路径中确实包含"AI插件"而不是"待办事项管理"
                                if 'AI插件' in candidate_path and '待办事项管理' not in candidate_path:
                                    plugin_path = candidate_path
                                    print(f"通过搜索找到AI插件文件: {plugin_path}")
                                    break
                        if plugin_path:
                            break
            
            # 明确的错误处理：如果所有路径都找不到插件文件
            if not plugin_path:
                error_msg = "找不到AI插件文件！\n\n已检查的路径：\n"
                for path in plugin_paths:
                    error_msg += f"• {path}\n"
                error_msg += "\n请确保以下文件之一存在：\n• ui_plugin_pyqt5.py (开发环境)\n• AI插件.exe (打包环境)"
                QMessageBox.warning(self, "警告", error_msg)
                print("启动失败：找不到插件文件")
                return
            
            # 如果已经有插件在运行，先关闭它
            if hasattr(self, 'ai_plugin_process') and self.ai_plugin_process:
                try:
                    print("关闭已运行的插件进程...")
                    self.ai_plugin_process.terminate()
                    self.ai_plugin_process.wait(timeout=3)
                    print("插件进程已关闭")
                except Exception as close_error:
                    print(f"关闭插件进程时出错: {close_error}")
                    try:
                        self.ai_plugin_process.kill()
                        print("强制关闭插件进程")
                    except:
                        pass
                finally:
                    self.ai_plugin_process = None
            
            # 启动AI插件
            print(f"准备启动插件: {plugin_path}")
            
            # 最终验证：确保要启动的文件确实是AI插件而不是待办事项管理
            if plugin_path.endswith('.exe'):
                filename = os.path.basename(plugin_path)
                if '待办事项管理' in filename:
                    QMessageBox.critical(self, "错误", f"检测到错误的文件：{filename}\nAI按钮应该启动AI插件.exe，而不是待办事项管理.exe")
                    print(f"错误：尝试启动错误的文件 {filename}")
                    return
                elif 'AI插件' not in filename:
                    QMessageBox.critical(self, "错误", f"检测到未知文件：{filename}\nAI按钮应该启动AI插件.exe")
                    print(f"错误：尝试启动未知文件 {filename}")
                    return
                
                # 打包后的exe文件
                print(f"启动AI插件exe文件: {plugin_path}")
                try:
                    # 不使用PIPE，避免阻塞
                    self.ai_plugin_process = subprocess.Popen(
                        [plugin_path],
                        creationflags=subprocess.CREATE_NEW_CONSOLE if os.name == 'nt' else 0
                    )
                    print(f"AI插件exe启动成功，进程ID: {self.ai_plugin_process.pid}")
                except Exception as exe_error:
                    print(f"AI插件exe启动失败: {exe_error}")
                    # 尝试不使用CREATE_NEW_CONSOLE
                    try:
                        print("尝试不使用CREATE_NEW_CONSOLE启动...")
                        self.ai_plugin_process = subprocess.Popen([plugin_path])
                        print(f"不使用CREATE_NEW_CONSOLE启动成功，进程ID: {self.ai_plugin_process.pid}")
                    except Exception as e2:
                        print(f"所有启动方式都失败: {e2}")
                        QMessageBox.critical(self, "错误", f"无法启动AI插件.exe文件：\n{str(e2)}")
                        return
            else:
                # 开发环境的py文件
                print(f"启动py文件: {plugin_path}")
                try:
                    self.ai_plugin_process = subprocess.Popen(
                        [sys.executable, plugin_path],
                        creationflags=subprocess.CREATE_NEW_CONSOLE if os.name == 'nt' else 0
                    )
                    print(f"py文件启动成功，进程ID: {self.ai_plugin_process.pid}")
                except Exception as py_error:
                    print(f"py文件启动失败: {py_error}")
                    QMessageBox.critical(self, "错误", f"无法启动AI插件.py文件：\n{str(py_error)}")
                    return
            
            # 验证进程是否成功启动
            if self.ai_plugin_process and self.ai_plugin_process.poll() is None:
                print(f"AI插件启动成功！进程ID: {self.ai_plugin_process.pid}")
                print(f"使用的插件路径: {plugin_path}")
            else:
                print("AI插件启动失败：进程对象无效或已退出")
                QMessageBox.warning(self, "警告", "AI插件启动失败，请检查插件文件是否完整")
                self.ai_plugin_process = None
            
        except Exception as e:
            QMessageBox.critical(self, "错误", f"启动AI插件时发生未知错误：\n{str(e)}")
            print(f"启动AI插件失败: {str(e)}")
            import traceback
            traceback.print_exc()
            self.ai_plugin_process = None
    
    def showTodoContextMenu(self, position):
        # 暂时禁用悬停检测和自动隐藏检测，避免在右键操作时意外触发显示或隐藏
        if self.is_hidden:
            self.hover_timer.stop()
            self.mouse_hovering = False
        
        # 标记用户正在交互，防止自动隐藏
        self.user_interacting = True
        
        # 创建右键菜单
        menu = QMenu(self)
        menu.setStyleSheet(f"""
            QMenu {{
                background-color: white;
                border: 1px solid {COLORS['border']};
                border-radius: 8px;
                padding: 5px;
            }}
            QMenu::item {{
                padding: 8px 25px;
                border-radius: 4px;
                color: {COLORS['text_primary']};
            }}
            QMenu::item:selected {{
                background-color: {COLORS['primary']};
                color: white;
            }}
        """)
        
        # 获取当前选定的项
        selected_row = self.todo_list_widget.currentRow()
        
        # 将"标记为完成"放在第一位
        complete_action = menu.addAction("标记为完成")
        
        # 根据当前状态显示不同的重要标记选项
        if selected_row >= 0 and self.todo_list[selected_row][2]:  # 如果已经是重要事项
            important_action = menu.addAction("取消重要标记")
        else:
            important_action = menu.addAction("标记为重要 (置顶)")
            
        edit_action = menu.addAction("编辑")
        delete_action = menu.addAction("删除")
        
        # 显示菜单并获取用户选择
        action = menu.exec_(self.todo_list_widget.mapToGlobal(position))
        
        if action == complete_action:
            self.markAsCompleted(selected_row)
        elif action == important_action:
            self.toggleImportant(selected_row)
        elif action == edit_action:
            self.editTodo(selected_row)
        elif action == delete_action:
            self.deleteTodo(selected_row)
        else:
            # 用户没有选择任何操作（点击了菜单外部或按了ESC），延迟重置用户交互状态
            QTimer.singleShot(500, lambda: setattr(self, 'user_interacting', False))
    
    def showCompletedContextMenu(self, position):
        # 暂时禁用悬停检测和自动隐藏检测，避免在右键操作时意外触发显示或隐藏
        if self.is_hidden:
            self.hover_timer.stop()
            self.mouse_hovering = False
        
        # 标记用户正在交互，防止自动隐藏
        self.user_interacting = True
        
        # 创建右键菜单
        menu = QMenu(self)
        menu.setStyleSheet(f"""
            QMenu {{
                background-color: white;
                border: 1px solid {COLORS['border']};
                border-radius: 8px;
                padding: 5px;
            }}
            QMenu::item {{
                padding: 8px 25px;
                border-radius: 4px;
                color: {COLORS['text_primary']};
            }}
            QMenu::item:selected {{
                background-color: {COLORS['primary']};
                color: white;
            }}
        """)
        
        restore_action = menu.addAction("恢复为待办")
        
        # 获取当前选定的项
        selected_row = self.completed_list_widget.currentRow()
        

            
        edit_action = menu.addAction("编辑")  # 添加编辑选项
        delete_action = menu.addAction("删除")
        if selected_row >= 0:
            action = menu.exec_(self.completed_list_widget.mapToGlobal(position))
            
            if action == restore_action:
                self.restoreTodo(selected_row)
            elif action == edit_action:
                self.editCompletedTodo(selected_row)  # 添加编辑已完成事项的方法
            elif action == delete_action:
                self.deleteCompleted(selected_row)
            else:
                # 用户没有选择任何操作（点击了菜单外部或按了ESC），延迟重置用户交互状态
                QTimer.singleShot(500, lambda: setattr(self, 'user_interacting', False))
        else:
            # 没有选中项，延迟重置用户交互状态
            QTimer.singleShot(500, lambda: setattr(self, 'user_interacting', False))
    
    def markAsCompleted(self, index):
        if 0 <= index < len(self.todo_list):
            # 在数据库中更新状态
            conn = sqlite3.connect(self.db_file)
            cursor = conn.cursor()
            
            # 获取待办事项内容和日期时间
            content, datetime_str, is_important, due_date = self.todo_list[index]
            
            # 获取当前时间作为提醒时间
            reminder_time = QDateTime.currentDateTime().toString("yyyy-MM-dd HH:mm")
            
            # 更新为已完成，并记录提醒时间
            cursor.execute(
                "UPDATE todos SET completed = 1, reminder_time = ? WHERE content = ? AND datetime = ? AND completed = 0", 
                (reminder_time, content, datetime_str)
            )
            
            # 更新全局版本号
            current_time = QDateTime.currentDateTime().toString("yyyy-MM-dd HH:mm:ss")
            cursor.execute(
                "UPDATE database_version SET version = version + 1, last_updated = ? WHERE id = 1",
                (current_time,)
            )
            
            conn.commit()
            conn.close()
            
            # 刷新列表（重要事项会自动置顶）
            self.loadData()
            
            # 延迟重置用户交互状态，允许自动隐藏
            QTimer.singleShot(1000, lambda: setattr(self, 'user_interacting', False))
            
            # 自动上传到远程服务器（版本号增加时触发备份）
            if self.auto_upload_enabled:
                self.auto_upload_to_remote()
    

    
    def restoreTodo(self, index):
        if 0 <= index < len(self.completed_list):
            # 在数据库中更新状态
            conn = sqlite3.connect(self.db_file)
            cursor = conn.cursor()
            
            # 获取已完成事项内容和日期时间
            content, datetime_str, is_important, completion_datetime, due_date = self.completed_list[index]
            
            # 更新为未完成，并清除完成时间
            cursor.execute(
                "UPDATE todos SET completed = 0, reminder_time = NULL WHERE content = ? AND datetime = ? AND completed = 1", 
                (content, datetime_str)
            )
            
            # 更新全局版本号
            current_time = QDateTime.currentDateTime().toString("yyyy-MM-dd HH:mm:ss")
            cursor.execute(
                "UPDATE database_version SET version = version + 1, last_updated = ? WHERE id = 1",
                (current_time,)
            )
            
            conn.commit()
            conn.close()
            
            # 刷新列表
            self.loadData()
            
            # 延迟重置用户交互状态，允许自动隐藏
            QTimer.singleShot(1000, lambda: setattr(self, 'user_interacting', False))
            
            # 自动上传到远程服务器
            if self.auto_upload_enabled:
                self.auto_upload_to_remote()
    
    def editTodo(self, index):
        # 如果窗口处于隐藏状态，先恢复显示
        if self.is_hidden:
            self.show_full_window()
            return
        
        if 0 <= index < len(self.todo_list):
            # 获取当前内容和日期时间
            current_content, current_datetime, is_important, due_date = self.todo_list[index]
            
            # 创建一个QDateTime对象
            dt = QDateTime.fromString(current_datetime, "yyyy-MM-dd HH:mm")
            
            # 创建编辑对话框，传递due_date作为提醒时间的默认值
            dialog = TodoEditDialog(self, current_content, dt, due_date=due_date)
            
            # 显示对话框并获取结果
            if dialog.exec_():
                new_content, new_dt, reminder_time = dialog.getInputs()
                if new_content.strip():
                    # 在数据库中更新内容
                    conn = sqlite3.connect(self.db_file)
                    cursor = conn.cursor()
                    
                    # 先查找待办事项的ID
                    cursor.execute(
                        "SELECT id FROM todos WHERE content = ? AND datetime = ? AND completed = 0", 
                        (current_content, current_datetime)
                    )
                    result = cursor.fetchone()
                    
                    if result:
                        todo_id = result[0]
                        # 使用ID作为更新条件，而不是内容和日期时间
                        cursor.execute(
                            "UPDATE todos SET content = ?, datetime = ?, due_date = ? WHERE id = ?", 
                            (new_content.strip(), new_dt.toString("yyyy-MM-dd HH:mm"), reminder_time, todo_id)
                        )
                    else:
                        print(f"警告：未找到待编辑的待办事项记录")
                    
                    # 更新全局版本号
                    current_time = QDateTime.currentDateTime().toString("yyyy-MM-dd HH:mm:ss")
                    cursor.execute(
                        "UPDATE database_version SET version = version + 1, last_updated = ? WHERE id = 1",
                        (current_time,)
                    )
                    
                    conn.commit()
                    conn.close()
                    
                    # 刷新列表（重要事项会自动置顶）
                    self.loadData()
                    
                    # 延迟重置用户交互状态，允许自动隐藏
                    QTimer.singleShot(1000, lambda: setattr(self, 'user_interacting', False))
                    
                    # 自动上传到远程服务器
                    if self.auto_upload_enabled:
                        self.auto_upload_to_remote()
            else:
                # 用户取消编辑，延迟重置用户交互状态，确保对话框完全关闭后再重置
                QTimer.singleShot(500, lambda: setattr(self, 'user_interacting', False))
    
    def editCompletedTodo(self, index):
        # 如果窗口处于隐藏状态，先恢复显示
        if self.is_hidden:
            self.show_full_window()
            return
        
        if 0 <= index < len(self.completed_list):
            # 获取当前内容和日期时间
            current_content, current_datetime, is_important, completion_datetime, due_date = self.completed_list[index]
            
            # 创建一个QDateTime对象
            dt = QDateTime.fromString(current_datetime, "yyyy-MM-dd HH:mm")
            
            # 创建编辑对话框，优先使用completion_datetime作为提醒时间，如果没有则使用due_date
            dialog = TodoEditDialog(self, current_content, dt, reminder_time=completion_datetime, due_date=due_date)
            
            # 显示对话框并获取结果
            if dialog.exec_():
                new_content, new_dt, new_completion_dt = dialog.getInputs()
                if new_content.strip():
                    # 在数据库中更新内容
                    conn = sqlite3.connect(self.db_file)
                    cursor = conn.cursor()
                    
                    # 先查找已完成待办事项的ID
                    cursor.execute(
                        "SELECT id FROM todos WHERE content = ? AND datetime = ? AND completed = 1", 
                        (current_content, current_datetime)
                    )
                    result = cursor.fetchone()
                    
                    if result:
                        todo_id = result[0]
                        # 使用ID作为更新条件，而不是内容和日期时间
                        cursor.execute(
                            "UPDATE todos SET content = ?, datetime = ?, due_date = ? WHERE id = ?", 
                            (new_content.strip(), new_dt.toString("yyyy-MM-dd HH:mm"), new_completion_dt, todo_id)
                        )
                    else:
                        print(f"警告：未找到待编辑的已完成事项记录")
                    
                    # 更新全局版本号
                    current_time = QDateTime.currentDateTime().toString("yyyy-MM-dd HH:mm:ss")
                    cursor.execute(
                        "UPDATE database_version SET version = version + 1, last_updated = ?",
                        (current_time,)
                    )
                    
                    conn.commit()
                    conn.close()
                    
                    # 刷新列表
                    self.loadData()
                    
                    # 延迟重置用户交互状态，允许自动隐藏
                    QTimer.singleShot(1000, lambda: setattr(self, 'user_interacting', False))
                    
                    # 自动上传到远程服务器
                    if self.auto_upload_enabled:
                        self.auto_upload_to_remote()
            else:
                # 用户取消编辑，延迟重置用户交互状态，确保对话框完全关闭后再重置
                QTimer.singleShot(500, lambda: setattr(self, 'user_interacting', False))
    
    def deleteTodo(self, index):
        # 如果窗口处于隐藏状态，先恢复显示
        if self.is_hidden:
            self.show_full_window()
            return
        
        if 0 <= index < len(self.todo_list):
            # 创建确认对话框
            confirm_dialog = QMessageBox(self)
            confirm_dialog.setWindowTitle(f"确认删除 {APP_VERSION}")
            confirm_dialog.setText("确定要删除这个待办事项吗?")
            confirm_dialog.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
            confirm_dialog.setDefaultButton(QMessageBox.No)
            
            # 设置对话框图标
            icon_path = APP_ICON
            if icon_path and os.path.exists(icon_path):
                confirm_dialog.setWindowIcon(QIcon(icon_path))
            
            # 在显示前调整确认对话框的按钮宽度
            for button in confirm_dialog.buttons():
                button.setMinimumWidth(160)  # 将最小宽度加倍
                button.setStyleSheet("""
                    min-width: 160px;
                    text-align: center;
                """)
            
            # 显示对话框并获取结果
            result = confirm_dialog.exec_()
            
            if result == QMessageBox.Yes:
                try:
                    # 在数据库中删除该条目
                    conn = sqlite3.connect(self.db_file)
                    cursor = conn.cursor()
                    
                    content, datetime_str, is_important, due_date = self.todo_list[index]
                    
                    # 更新全局版本号
                    current_time = QDateTime.currentDateTime().toString("yyyy-MM-dd HH:mm:ss")
                    cursor.execute(
                        "UPDATE database_version SET version = version + 1, last_updated = ? WHERE id = 1",
                        (current_time,)
                    )
                    
                    # 删除待办事项
                    cursor.execute(
                        "DELETE FROM todos WHERE content = ? AND datetime = ? AND completed = 0", 
                        (content, datetime_str)
                    )
                    conn.commit()
                    conn.close()
                    
                    # 刷新列表
                    self.loadData()
                    print(f"已成功删除待办事项: {content}")
                    
                    # 延迟重置用户交互状态，允许自动隐藏
                    QTimer.singleShot(2000, lambda: setattr(self, 'user_interacting', False))
                    
                    # 自动上传到远程服务器
                    if self.auto_upload_enabled:
                        self.auto_upload_to_remote()
                except Exception as e:
                    print(f"删除待办事项时出错: {e}")
                    QMessageBox.warning(self, "删除失败", f"无法删除该待办事项: {str(e)}")
    
    def deleteCompleted(self, index):
        # 如果窗口处于隐藏状态，先恢复显示
        if self.is_hidden:
            self.show_full_window()
            return
        
        if 0 <= index < len(self.completed_list):
            # 创建确认对话框
            confirm_dialog = QMessageBox(self)
            confirm_dialog.setWindowTitle(f"确认删除 {APP_VERSION}")
            confirm_dialog.setText("确定要删除这个已完成事项吗?")
            confirm_dialog.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
            confirm_dialog.setDefaultButton(QMessageBox.No)
            
            # 设置对话框图标
            icon_path = APP_ICON
            if icon_path and os.path.exists(icon_path):
                confirm_dialog.setWindowIcon(QIcon(icon_path))
            
            # 在显示前调整确认对话框的按钮宽度
            for button in confirm_dialog.buttons():
                button.setMinimumWidth(160)  # 将最小宽度加倍
                button.setStyleSheet("""
                    min-width: 160px;
                    text-align: center;
                """)
            
            # 显示对话框并获取结果
            result = confirm_dialog.exec_()
            
            if result == QMessageBox.Yes:
                try:
                    # 在数据库中删除该条目
                    conn = sqlite3.connect(self.db_file)
                    cursor = conn.cursor()
                    
                    content, datetime_str, is_important, completion_datetime, due_date = self.completed_list[index]
                    
                    # 更新全局版本号
                    current_time = QDateTime.currentDateTime().toString("yyyy-MM-dd HH:mm:ss")
                    cursor.execute(
                        "UPDATE database_version SET version = version + 1, last_updated = ? WHERE id = 1",
                        (current_time,)
                    )
                    
                    # 删除已完成事项
                    cursor.execute(
                        "DELETE FROM todos WHERE content = ? AND datetime = ? AND completed = 1", 
                        (content, datetime_str)
                    )
                    conn.commit()
                    conn.close()
                    
                    # 刷新列表
                    self.loadData()
                    print(f"已成功删除已完成事项: {content}")
                    
                    # 延迟重置用户交互状态，允许自动隐藏
                    QTimer.singleShot(2000, lambda: setattr(self, 'user_interacting', False))
                    
                    # 自动上传到远程服务器
                    if self.auto_upload_enabled:
                        self.auto_upload_to_remote()
                except Exception as e:
                    print(f"删除已完成事项时出错: {e}")
                    QMessageBox.warning(self, "删除失败", f"无法删除该已完成事项: {str(e)}")
    
    def toggleLock(self, checked):
        """切换锁定状态 - 改为锁定到桌面底层"""
        # 如果窗口处于隐藏状态，先恢复显示
        if self.is_hidden:
            self.show_full_window()
            return
        
        if checked:
            # 直接锁定，不需要密码
            self.lock_to_desktop()
        else:
            # 直接解锁，不需要密码
            self.unlock_desktop()
    
    def lock_to_desktop(self):
        """锁定到桌面底层"""
        self.is_interface_locked = True
        self.update_lock_button_icon()
        
        # 停止自动隐藏定时器，防止锁定后界面被隐藏
        self.auto_hide_timer.stop()
        
        # 重置鼠标光标为默认箭头光标
        self.setCursor(Qt.ArrowCursor)
        
        # 设置窗口属性：不接受焦点
        self.setAttribute(Qt.WA_ShowWithoutActivating, True)
        
        # 使用Windows API确保窗口置于桌面最底层
        try:
            import ctypes
            # 定义常量
            HWND_BOTTOM = 1  # 窗口置于底层
            SWP_NOSIZE = 0x0001  # 保持大小不变
            SWP_NOMOVE = 0x0002  # 保持位置不变
            SWP_NOACTIVATE = 0x0010  # 不激活窗口
            SWP_SHOWWINDOW = 0x0040  # 显示窗口
            
            # 设置窗口标志：无边框、工具窗口
            self.setWindowFlags(Qt.FramelessWindowHint | Qt.Tool)
            self.show()  # 应用标志更改
            
            # 获取窗口句柄
            hwnd = int(self.winId())
            
            # 设置窗口位置到底层
            result = ctypes.windll.user32.SetWindowPos(
                hwnd,
                HWND_BOTTOM,
                0, 0, 0, 0,
                SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_SHOWWINDOW
            )
            
            if result == 0:
                raise Exception("SetWindowPos返回失败")
                
            print(f"已使用Windows API将窗口置于桌面最底层，结果：{result}")
            
            # 额外调用lower确保效果
            self.lower()
            
        except Exception as e:
            print(f"设置窗口到底层时出错: {e}")
            # 如果API调用失败，使用Qt的方法作为备选
            self.setWindowFlags(Qt.FramelessWindowHint | Qt.Tool)
            self.show()
            self.lower()
        
        # 保存锁定状态
        self.settings.setValue("is_interface_locked", True)
        print("已锁定到桌面底层，自动隐藏已禁用")
    
    def unlock_desktop(self):
        """解锁"""
        # 直接解锁，不需要密码验证
        self.is_interface_locked = False
        self.update_lock_button_icon()
        
        # 重新启动自动隐藏定时器
        self.auto_hide_timer.start(50)
        
        # 恢复窗口标志：无边框、置顶
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool)
        
        # 恢复窗口属性
        self.setAttribute(Qt.WA_ShowWithoutActivating, False)
        
        # 将窗口提升到前台
        self.raise_()
        self.raise_()
        self.activateWindow()
        
        # 确保窗口可见
        self.show()
        
        # 重置鼠标光标为默认箭头光标
        self.setCursor(Qt.ArrowCursor)
        
        # 保存锁定状态
        self.settings.setValue("is_interface_locked", False)
        print("已解锁，自动隐藏已恢复")
    
    def update_lock_button_icon(self):
        """更新锁定按钮图标"""
        if self.is_interface_locked:
            # 锁定状态使用解锁图标
            unlock_icon = get_embedded_svg_icon('unlock')
            if unlock_icon:
                self.lock_button.setIcon(unlock_icon)
            else:
                self.lock_button.setText("🔓")  # Fallback to emoji if SVG not found
        else:
            # 未锁定状态使用锁定图标
            lock_icon = get_embedded_svg_icon('lock')
            if lock_icon:
                self.lock_button.setIcon(lock_icon)
            else:
                self.lock_button.setText("🔒")  # Fallback to emoji if SVG not found
    
    def is_interactive_widget(self, widget):
        """判断小部件是否为互动元素"""
        if not widget:
            return False
        
        # 检查小部件类型
        widget_type = type(widget).__name__
        
        # 互动元素类型列表
        interactive_types = [
            'QPushButton',      # 按钮
            'QLineEdit',        # 输入框
            'QTextEdit',        # 文本编辑框
            'QListWidget',      # 列表控件
            'QCheckBox',        # 复选框
            'QDateTimeEdit',    # 日期时间编辑框
            'QDialogButtonBox', # 对话框按钮组
            'QMenu',            # 菜单
            'TodoItemWidget',   # 自定义待办项组件
        ]
        
        # 检查是否为互动类型
        if widget_type in interactive_types:
            return True
        
        # 检查父级小部件
        parent = widget.parent()
        while parent:
            parent_type = type(parent).__name__
            if parent_type in interactive_types:
                return True
            parent = parent.parent()
        
        return False
    
    def hide_todo_interface(self):
        """隐藏待办界面"""
        # 保存当前窗口大小
        self.settings.setValue("geometry", self.geometry())
        
        # 隐藏输入框、列表等界面元素
        self.todo_input.setVisible(False)
        self.todo_tab_button.setVisible(False)
        self.completed_tab_button.setVisible(False)
        self.stacked_widget.setVisible(False)
        
        # 显示锁定提示
        if hasattr(self, 'lock_label'):
            self.lock_label.setVisible(True)
            if hasattr(self, 'lock_hint'):
                self.lock_hint.setVisible(True)
        else:
            # 创建更美观的锁定提示
            self.lock_label = QLabel("🔒 界面已锁定")
            self.lock_label.setAlignment(Qt.AlignCenter)
            self.lock_label.setStyleSheet("""
                font-size: 18px; 
                font-weight: bold;
                color: #666;
                padding: 20px;
                background-color: #f8f9fa;
                border: 2px solid #dee2e6;
                border-radius: 10px;
                margin: 10px;
            """)
            
            # 创建提示说明
            self.lock_hint = QLabel("点击解锁按钮输入密码以恢复界面")
            self.lock_hint.setAlignment(Qt.AlignCenter)
            self.lock_hint.setStyleSheet("""
                font-size: 12px; 
                color: #999;
                margin-top: 10px;
            """)
            
            # 添加到主布局（只在第一次创建时添加）
            main_layout = self.centralWidget().layout()
            main_layout.addWidget(self.lock_label)
            main_layout.addWidget(self.lock_hint)
    
    def show_todo_interface(self):
        """显示待办界面"""
        # 显示所有界面元素
        self.todo_input.setVisible(True)
        self.todo_tab_button.setVisible(True)
        self.completed_tab_button.setVisible(True)
        self.stacked_widget.setVisible(True)
        
        # 隐藏锁定提示
        if hasattr(self, 'lock_label'):
            self.lock_label.setVisible(False)
        if hasattr(self, 'lock_hint'):
            self.lock_hint.setVisible(False)
        
        # 恢复用户设置的窗口大小
        if self.settings.contains("geometry"):
            geometry = self.settings.value("geometry")
            if isinstance(geometry, QRect):
                self.setGeometry(geometry)
            else:
                # 如果没有保存的几何信息，使用默认大小
                self.resize(900, 650)
        else:
            # 如果没有保存的几何信息，使用默认大小
            self.resize(900, 650)
    
    def loadData(self):
        try:
            # 从数据库加载数据
            conn = sqlite3.connect(self.db_file)
            cursor = conn.cursor()
            
            # 获取未完成的待办事项（加上日期时间、重要标记和完成时间）
            # 重要事项优先显示，然后按时间降序排列
            cursor.execute("""
                SELECT content, datetime, important, due_date FROM todos 
                WHERE completed = 0 
                ORDER BY important DESC, datetime DESC
            """)
            self.todo_list = [(row[0], row[1], bool(row[2]), row[3]) for row in cursor.fetchall()]
            print(f"从数据库加载了 {len(self.todo_list)} 条待办事项")
            
            # 获取已完成的事项（加上日期时间、重要标记、完成时间和截止时间）
            # 按完成时间降序排列，最近完成的排在上面
            cursor.execute("""
                SELECT content, datetime, important, reminder_time, due_date FROM todos 
                WHERE completed = 1 
                ORDER BY reminder_time DESC
            """)
            self.completed_list = [(row[0], row[1], bool(row[2]), row[3], row[4]) for row in cursor.fetchall()]
            print(f"从数据库加载了 {len(self.completed_list)} 条已完成事项")
            
            conn.close()
            
            # 更新列表显示
            self.updateTodoList()
            self.updateCompletedList()
            
            # 保持当前选中的列表视图状态不变
            # 不需要修改按钮状态，因为按钮状态与堆叠窗口状态已经绑定
            
        except sqlite3.Error as e:
            error_msg = f"数据库错误: {str(e)}"
            print(error_msg)
            QMessageBox.warning(self, "加载失败", error_msg)
        except Exception as e:
            error_msg = f"加载数据时发生未知错误: {str(e)}"
            print(error_msg)
            QMessageBox.warning(self, "加载失败", error_msg)
    
    def saveWindowState(self):
        """保存窗口位置、大小和状态"""
        self.settings.setValue("geometry", self.geometry())
        self.settings.setValue("is_interface_locked", self.is_interface_locked)
        # 保存当前显示的列表索引
        self.settings.setValue("current_list_index", self.stacked_widget.currentIndex())
        print("已保存窗口状态")
    
    def restoreWindowState(self):
        """恢复窗口位置、大小和状态"""
        # 恢复窗口位置和大小
        if self.settings.contains("geometry"):
            geometry = self.settings.value("geometry")
            if isinstance(geometry, QRect):  # QRect对象
                self.setGeometry(geometry)
            print("已恢复窗口位置和大小")
        
        # 恢复锁定状态
        if self.settings.contains("is_interface_locked"):
            is_interface_locked = self.settings.value("is_interface_locked")
            # 将字符串'true'/'false'转换为布尔值
            if isinstance(is_interface_locked, str):
                is_interface_locked = (is_interface_locked.lower() == 'true')
            elif not isinstance(is_interface_locked, bool):
                is_interface_locked = bool(is_interface_locked)
                
            print(f"恢复桌面锁定状态: {is_interface_locked}")
            
            if is_interface_locked:
                self.lock_button.setChecked(True)
                # 直接设置锁定状态
                self.is_interface_locked = True
                self.update_lock_button_icon()
                # 停止自动隐藏定时器，防止锁定后界面被隐藏
                self.auto_hide_timer.stop()
                # 应用桌面锁定设置
                self.setWindowFlags(Qt.FramelessWindowHint | Qt.Tool)
                self.setAttribute(Qt.WA_ShowWithoutActivating, True)
                self.lower()  # 移动到桌面底层
                self.show()  # 需要重新显示窗口以应用标志
                

        
        # 恢复当前显示的列表
        if self.settings.contains("current_list_index"):
            list_index = self.settings.value("current_list_index")
            # 如果是字符串，转换为整数
            if isinstance(list_index, str):
                list_index = int(list_index)
            elif not isinstance(list_index, int):
                list_index = int(list_index)
            
            # 设置对应的显示状态
            if list_index == 1:  # 已完成列表
                self.stacked_widget.setCurrentIndex(1)
                self.todo_tab_button.setChecked(False)
                self.completed_tab_button.setChecked(True)
            else:  # 默认或无效索引，显示待办列表
                self.stacked_widget.setCurrentIndex(0)
                self.todo_tab_button.setChecked(True)
                self.completed_tab_button.setChecked(False)

    # 完全重写窗口鼠标事件处理函数，简化调整大小的逻辑
    def mousePressEvent(self, event):
        """鼠标按下事件处理"""
        if event.button() == Qt.LeftButton:
            # 设置用户交互标志，防止自动隐藏
            self.user_interacting = True
            
            # 如果锁定到桌面，只允许互动元素响应
            if self.is_interface_locked:
                # 检查是否点击的是互动元素（按钮、输入框、列表等）
                clicked_widget = self.childAt(event.pos())
                if clicked_widget and self.is_interactive_widget(clicked_widget):
                    # 允许互动元素响应
                    super().mousePressEvent(event)
                else:
                    # 非互动元素，阻止移动和调整大小
                    event.accept()
                    return
            else:
                # 未锁定时，正常处理
                # 检查是否在标题栏区域
                if event.y() < 30:  # 标题栏高度
                    self.is_dragging = True
                    self.drag_position = event.globalPos() - self.frameGeometry().topLeft()
                    event.accept()
                else:
                    # 检查是否在调整大小区域
                    window_rect = self.rect()
                    resize_margin = 5
                    
                    # 检查是否在边缘区域
                    if (event.x() <= resize_margin or 
                        event.x() >= window_rect.width() - resize_margin or
                        event.y() <= resize_margin or 
                        event.y() >= window_rect.height() - resize_margin):
                        
                        self.resizing = True
                        self.resize_start_position = event.globalPos()
                        self.start_geometry = self.geometry()
                        event.accept()
                    else:
                        # 普通点击，传递给父类处理
                        super().mousePressEvent(event)
        else:
            super().mousePressEvent(event)

    def mouseMoveEvent(self, event):
        """鼠标移动事件处理"""
        # 如果锁定到桌面，阻止所有移动和调整大小操作
        if self.is_interface_locked:
            # 只允许互动元素内的鼠标移动
            hovered_widget = self.childAt(event.pos())
            if hovered_widget and self.is_interactive_widget(hovered_widget):
                # 互动元素内，设置正常光标
                self.setCursor(Qt.ArrowCursor)
            else:
                # 非互动元素，设置不允许移动的光标
                self.setCursor(Qt.ArrowCursor)  # 使用普通箭头，不显示禁止符号
            
            # 阻止事件传递，避免其他处理逻辑
            event.accept()
            return
        
        # 如果窗口处于隐藏状态，检查鼠标悬停
        if self.is_hidden:
            self.check_global_mouse_hover()
        
        if event.buttons() & Qt.LeftButton:
            if self.resizing:
                # 计算大小增量
                if self.resize_start_position is not None and self.start_geometry is not None:
                    delta = event.globalPos() - self.resize_start_position
                    new_width = max(self.minimumWidth(), self.start_geometry.width() + delta.x())
                    new_height = max(self.minimumHeight(), self.start_geometry.height() + delta.y())
                    self.resize(new_width, new_height)
            else:
                # 移动窗口
                if self.drag_position is not None:
                    self.move(event.globalPos() - self.drag_position)
        else:
            # 检查鼠标是否在右下角
            rect = self.rect()
            bottomRight = rect.bottomRight()
            resizeArea = QRect(bottomRight.x() - 20, bottomRight.y() - 20, 20, 20)
            
            if not self.is_interface_locked and resizeArea.contains(event.pos()):
                self.setCursor(Qt.SizeFDiagCursor)
            else:
                self.setCursor(Qt.ArrowCursor)

    def mouseReleaseEvent(self, event):
        """鼠标释放事件"""
        # 如果锁定到桌面，检查是否在交互式控件上释放鼠标
        if self.is_interface_locked:
            # 检查是否点击的是互动元素
            released_widget = self.childAt(event.pos())
            if released_widget and self.is_interactive_widget(released_widget):
                # 允许互动元素响应
                super().mouseReleaseEvent(event)
            else:
                # 非互动元素，阻止事件
                event.accept()
            
            # 重置为默认光标
            self.setCursor(Qt.ArrowCursor)
            return
            
        # 非锁定状态下的正常处理
        if event.button() == Qt.LeftButton:
            # 使用深蓝色背景
            press_color = "rgba(67, 97, 238, 0.15)"  # 深蓝色的悬停/选中/按下背景色，调淡透明度
        if event.button() == Qt.LeftButton:
            if self.is_dragging:
                self.is_dragging = False
                self.drag_position = None
                # 延迟清除用户交互标志，给右键菜单足够的时间
                QTimer.singleShot(500, lambda: setattr(self, 'user_interacting', False))
                event.accept()
            elif self.resizing:
                self.resizing = False
                self.resize_start_position = None
                self.start_geometry = None
                # 延迟清除用户交互标志，给右键菜单足够的时间
                QTimer.singleShot(500, lambda: setattr(self, 'user_interacting', False))
                event.accept()
            else:
                # 普通点击，延迟清除用户交互标志
                QTimer.singleShot(500, lambda: setattr(self, 'user_interacting', False))
                super().mouseReleaseEvent(event)
        else:
            super().mouseReleaseEvent(event)
    
    def check_auto_hide(self):
        """检查是否需要自动隐藏"""
        if self.is_interface_locked:
            print("桌面已锁定，跳过自动隐藏检测")
            return
        
        # 如果正在执行动画，跳过检测
        if ((self.hide_animation and self.hide_animation.state() == QPropertyAnimation.Running) or
            (self.show_animation and self.show_animation.state() == QPropertyAnimation.Running)):
            print("正在执行动画，跳过自动隐藏检测")
            return
        
        # 如果鼠标正在悬停，不允许隐藏
        if self.mouse_hovering:
            print("鼠标正在悬停，不允许隐藏")
            return
        
        # 如果用户正在与窗口交互，不允许隐藏
        if self.user_interacting:
            print("用户正在交互，不允许隐藏")
            return
        
        # 移除延迟检测，让窗口立即隐藏
        # 如果窗口刚显示不久，延迟检测，避免立即隐藏
        # if hasattr(self, 'last_show_time'):
        #     current_time = QDateTime.currentDateTime()
        #     time_diff = self.last_show_time.msecsTo(current_time)
        #     if time_diff < 2000:  # 2秒内不自动隐藏
        #         print(f"窗口刚显示 {time_diff}ms，延迟检测")
        #         return
        
        # 获取当前鼠标位置
        cursor_pos = QCursor.pos()
        
        # 获取窗口当前位置
        window_geometry = self.geometry()
        window_x = window_geometry.x()
        window_y = window_geometry.y()
        window_width = window_geometry.width()
        window_height = window_geometry.height()
        
        # 检查鼠标是否在窗口区域内（包括边缘区域）
        window_rect = QRect(window_x, window_y, window_width, window_height)
        
        print(f"自动隐藏检测 - 鼠标位置: {cursor_pos}, 窗口区域: {window_rect}")
        
        # 如果鼠标仍在窗口区域内，不隐藏
        if window_rect.contains(cursor_pos):
            print("鼠标仍在窗口区域内，不隐藏")
            return
        
        # 额外检查：如果鼠标在窗口附近（扩大检测区域），也不隐藏
        expanded_rect = QRect(window_x - 10, window_y - 10, window_width + 20, window_height + 20)
        if expanded_rect.contains(cursor_pos):
            print("鼠标在窗口附近区域，不隐藏")
            return
        
        print("鼠标已离开窗口区域，继续检查隐藏条件")
        
        # 获取屏幕几何信息
        screen = QApplication.primaryScreen()
        screen_geometry = screen.geometry()
        
        # 检查是否靠近左边缘
        if window_x <= self.auto_hide_threshold and not self.is_hidden:
            print(f"窗口靠近左边缘 (x={window_x}, threshold={self.auto_hide_threshold})")
            # 检查鼠标是否在隐藏区域附近，如果是则不隐藏
            hide_area = QRect(0, window_y, 50, window_height)  # 左侧50像素区域
            if not hide_area.contains(cursor_pos):
                print("鼠标不在左侧隐藏区域，执行隐藏到左侧")
                self.hide_to_left()
            else:
                print("鼠标在左侧隐藏区域，不隐藏")
        # 检查是否靠近右边缘
        elif (window_x + window_width) >= (screen_geometry.width() - self.auto_hide_threshold) and not self.is_hidden:
            print(f"窗口靠近右边缘 (x={window_x}, width={window_width}, screen_width={screen_geometry.width()})")
            # 检查鼠标是否在隐藏区域附近，如果是则不隐藏
            hide_area = QRect(screen_geometry.width() - 50, window_y, 50, window_height)  # 右侧50像素区域
            if not hide_area.contains(cursor_pos):
                print("鼠标不在右侧隐藏区域，执行隐藏到右侧")
                self.hide_to_right()
            else:
                print("鼠标在右侧隐藏区域，不隐藏")
        # 检查是否靠近顶部边缘
        elif window_y <= self.auto_hide_threshold and not self.is_hidden:
            print(f"窗口靠近顶部边缘 (y={window_y}, threshold={self.auto_hide_threshold})")
            # 检查鼠标是否在隐藏区域附近，如果是则不隐藏
            hide_area = QRect(window_x, 0, window_width, 50)  # 顶部50像素区域
            if not hide_area.contains(cursor_pos):
                print("鼠标不在顶部隐藏区域，执行隐藏到顶部")
                self.hide_to_top()
            else:
                print("鼠标在顶部隐藏区域，不隐藏")
        # 检查是否应该恢复显示
        elif self.is_hidden:
            print("窗口已隐藏，检查是否应该恢复显示")
            # 检查是否远离所有边缘
            away_from_left = window_x > self.auto_hide_threshold
            away_from_right = (window_x + window_width) < (screen_geometry.width() - self.auto_hide_threshold)
            away_from_top = window_y > self.auto_hide_threshold
            
            if away_from_left and away_from_right and away_from_top:
                print("窗口远离所有边缘，恢复显示")
                self.show_full_window()
            else:
                print("窗口仍在边缘，不恢复显示")
        else:
            print("窗口不在任何边缘，不执行隐藏")
    
    def hide_to_left(self):
        """隐藏到左侧"""
        print(f"尝试隐藏到左侧 - is_hidden: {self.is_hidden}")
        
        if self.is_hidden or (self.hide_animation and self.hide_animation.state() == QPropertyAnimation.Running):
            print("隐藏条件不满足，跳过隐藏")
            return
        
        print("开始隐藏到左侧")
        
        # 设置隐藏状态
        self.is_hidden = True
        
        # 隐藏到任务栏 - 设置窗口标志使图标不显示在任务栏，并置顶
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool)
        self.show()
        
        # 隐藏到托盘
        self.hide_to_tray()
        
        # 创建动画
        self.hide_animation = QPropertyAnimation(self, b"geometry")
        self.hide_animation.setDuration(self.animation_duration)
        self.hide_animation.setEasingCurve(QEasingCurve.OutQuart)  # 使用更平滑的缓动曲线
        self.hide_animation.setLoopCount(1)
                
        # 设置动画的起始和结束状态
        start_geometry = self.geometry()
        window_width = self.width()
        window_height = self.height()
        
        # 隐藏到左侧，只露出5像素
        end_geometry = QRect(-window_width + 5, start_geometry.y(), window_width, window_height)
        
        print(f"隐藏动画起始位置: {start_geometry}")
        print(f"隐藏动画结束位置: {end_geometry}")
        
        self.hide_animation.setStartValue(start_geometry)
        self.hide_animation.setEndValue(end_geometry)
        
        # 连接动画完成信号
        self.hide_animation.finished.connect(self.on_hide_animation_finished)
        
        # 开始动画
        self.hide_animation.start()
        
        print("窗口开始平滑隐藏到左侧")
    
    def on_hide_animation_finished(self):
        """隐藏动画完成后的回调"""
        print("窗口已自动隐藏到左侧")
    
    def hide_to_right(self):
        """隐藏到右侧"""
        if self.is_hidden or (self.hide_animation and self.hide_animation.state() == QPropertyAnimation.Running):
            return
        
        # 设置隐藏状态
        self.is_hidden = True
        
        # 隐藏到任务栏 - 设置窗口标志使图标不显示在任务栏，并置顶
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool)
        self.show()
        
        # 隐藏到托盘
        self.hide_to_tray()
        
        # 获取屏幕宽度
        screen = QApplication.primaryScreen()
        screen_width = screen.geometry().width()
        
        # 创建动画
        self.hide_animation = QPropertyAnimation(self, b"geometry")
        self.hide_animation.setDuration(self.animation_duration)
        self.hide_animation.setEasingCurve(QEasingCurve.OutQuart)  # 使用更平滑的缓动曲线
        self.hide_animation.setLoopCount(1)
                
        # 设置动画的起始和结束状态
        start_geometry = self.geometry()
        window_width = self.width()
        window_height = self.height()
        
        # 隐藏到右侧，只露出5像素
        end_geometry = QRect(screen_width - 5, start_geometry.y(), window_width, window_height)
        
        self.hide_animation.setStartValue(start_geometry)
        self.hide_animation.setEndValue(end_geometry)
        
        # 连接动画完成信号
        self.hide_animation.finished.connect(self.on_hide_animation_finished)
        
        # 开始动画
        self.hide_animation.start()
        
        print("窗口开始平滑隐藏到右侧")
    
    def hide_to_top(self):
        """隐藏到顶部"""
        if self.is_hidden or (self.hide_animation and self.hide_animation.state() == QPropertyAnimation.Running):
            return
        
        # 设置隐藏状态
        self.is_hidden = True
        
        # 隐藏到任务栏 - 设置窗口标志使图标不显示在任务栏，并置顶
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool)
        self.show()
        
        # 创建动画
        self.hide_animation = QPropertyAnimation(self, b"geometry")
        self.hide_animation.setDuration(self.animation_duration)
        self.hide_animation.setEasingCurve(QEasingCurve.OutQuart)  # 使用更平滑的缓动曲线
        self.hide_animation.setLoopCount(1)
                
        # 设置动画的起始和结束状态
        start_geometry = self.geometry()
        window_width = self.width()
        window_height = self.height()
        
        # 隐藏到顶部，只露出5像素
        end_geometry = QRect(start_geometry.x(), -window_height + 5, window_width, window_height)
        
        self.hide_animation.setStartValue(start_geometry)
        self.hide_animation.setEndValue(end_geometry)
        
        # 连接动画完成信号
        self.hide_animation.finished.connect(self.on_hide_animation_finished)
        
        # 开始动画
        self.hide_animation.start()
        
        print("窗口开始平滑隐藏到顶部")
    
    def show_full_window(self):
        """恢复显示完整窗口"""
        print(f"尝试显示完整窗口 - is_hidden: {self.is_hidden}")
        
        if not self.is_hidden or (self.show_animation and self.show_animation.state() == QPropertyAnimation.Running):
            print("显示条件不满足，跳过显示")
            return
        
        print("开始显示完整窗口")
        
        # 重置隐藏状态
        self.is_hidden = False
        
        # 先设置窗口标志，避免闪烁，始终隐藏任务栏图标
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool)
        self.setAttribute(Qt.WA_ShowWithoutActivating, False)  # 确保窗口可以激活
        
        # 获取屏幕几何信息
        screen = QApplication.primaryScreen()
        screen_geometry = screen.geometry()
        
        # 从窗口的实际大小获取尺寸
        window_width = self.width()
        window_height = self.height()
        
        # 如果当前窗口尺寸太小，使用最小尺寸
        if window_width < self.minimumSize().width():
            window_width = self.minimumSize().width()
        if window_height < self.minimumSize().height():
            window_height = self.minimumSize().height()
        
        # 根据当前窗口位置判断从哪个边缘弹出
        current_x = self.x()
        current_y = self.y()
        
        # 如果窗口在左侧边缘，从左侧弹出
        if current_x <= 0:
            x = 0
            y = current_y
        # 如果窗口在右侧边缘，从右侧弹出
        elif current_x + window_width >= screen_geometry.width():
            x = screen_geometry.width() - window_width
            y = current_y
        # 如果窗口在顶部边缘，从顶部弹出
        elif current_y <= 0:
            x = current_x
            y = 0
        # 否则使用当前位置
        else:
            x = current_x
            y = current_y
        
        # 确保窗口不会超出屏幕边界
        if x < 0:
            x = 0
        if y < 0:
            y = 0
        if x + window_width > screen_geometry.width():
            x = screen_geometry.width() - window_width
        if y + window_height > screen_geometry.height():
            y = screen_geometry.height() - window_height
        
        end_geometry = QRect(x, y, window_width, window_height)
        
        print(f"最终恢复位置: {end_geometry}")
        
        # 创建显示动画
        self.show_animation = QPropertyAnimation(self, b"geometry")
        self.show_animation.setDuration(self.animation_duration)
        self.show_animation.setEasingCurve(QEasingCurve.OutQuart)  # 使用更平滑的缓动曲线
        self.show_animation.setLoopCount(1)
                
        # 设置动画的起始和结束状态
        # 使用当前隐藏位置作为起始位置，确保动画从隐藏位置开始
        current_geometry = self.geometry()
        print(f"显示动画起始位置: {current_geometry}")
        print(f"显示动画结束位置: {end_geometry}")
        
        self.show_animation.setStartValue(current_geometry)
        self.show_animation.setEndValue(end_geometry)
        
        # 连接动画完成信号
        self.show_animation.finished.connect(self.on_show_animation_finished)
        
        # 开始动画
        self.show_animation.start()
        
        print("窗口开始平滑显示")
        
        # 清除悬停状态，停止悬停检测
        self.mouse_hovering = False
        self.hover_start_time = None
        self.hover_timer.stop()
        
        # 记录窗口显示时间，防止立即隐藏
        self.last_show_time = QDateTime.currentDateTime()
    
    def on_show_animation_finished(self):
        """显示动画完成后的回调"""
        print("窗口已恢复显示")
        # 确保窗口可见并置顶
        self.show()
        self.raise_()
        self.activateWindow()
        self.setFocus()  # 确保窗口获得焦点
    
    def check_mouse_hover(self):
        """检查鼠标悬停 - 只在软件隐藏位置的边缘时触发"""
        if not self.is_hidden:
            return
        
        # 获取鼠标位置
        cursor_pos = QCursor.pos()
        
        # 获取窗口当前几何信息（隐藏后的位置）
        window_geometry = self.geometry()
        
        # 获取屏幕几何信息
        screen = QApplication.primaryScreen()
        screen_geometry = screen.geometry()
        
        # 检查鼠标是否在软件隐藏位置的边缘
        # 左侧隐藏：检测鼠标是否在窗口右侧5像素区域内
        if window_geometry.x() < 0:  # 左侧隐藏
            edge_area = QRect(window_geometry.x() + window_geometry.width() - 5, 
                             window_geometry.y(), 5, window_geometry.height())
            if edge_area.contains(cursor_pos):
                if not self.mouse_hovering:
                    self.mouse_hovering = True
                    self.hover_start_time = QDateTime.currentDateTime()
                    print(f"检测到鼠标在左侧隐藏窗口边缘，位置: {cursor_pos}, 启动悬停检测")
                    self.hover_timer.start(100)
                return
            else:
                if self.mouse_hovering:
                    self.mouse_hovering = False
                    self.hover_start_time = None
                    self.hover_timer.stop()
                    print(f"鼠标离开左侧隐藏窗口边缘: {cursor_pos}")
                return
        
        # 右侧隐藏：检测鼠标是否在窗口左侧5像素区域内
        elif window_geometry.x() + window_geometry.width() > screen_geometry.width():  # 右侧隐藏
            edge_area = QRect(window_geometry.x(), 
                             window_geometry.y(), 5, window_geometry.height())
            if edge_area.contains(cursor_pos):
                if not self.mouse_hovering:
                    self.mouse_hovering = True
                    self.hover_start_time = QDateTime.currentDateTime()
                    print(f"检测到鼠标在右侧隐藏窗口边缘，位置: {cursor_pos}, 启动悬停检测")
                    self.hover_timer.start(100)
                return
            else:
                if self.mouse_hovering:
                    self.mouse_hovering = False
                    self.hover_start_time = None
                    self.hover_timer.stop()
                    print(f"鼠标离开右侧隐藏窗口边缘: {cursor_pos}")
                return
        
        # 顶部隐藏：检测鼠标是否在窗口底部5像素区域内
        elif window_geometry.y() < 0:  # 顶部隐藏
            edge_area = QRect(window_geometry.x(), 
                             window_geometry.y() + window_geometry.height() - 5, 
                             window_geometry.width(), 5)
            if edge_area.contains(cursor_pos):
                if not self.mouse_hovering:
                    self.mouse_hovering = True
                    self.hover_start_time = QDateTime.currentDateTime()
                    print(f"检测到鼠标在顶部隐藏窗口边缘，位置: {cursor_pos}, 启动悬停检测")
                    self.hover_timer.start(100)
                return
            else:
                if self.mouse_hovering:
                    self.mouse_hovering = False
                    self.hover_start_time = None
                    self.hover_timer.stop()
                    print(f"鼠标离开顶部隐藏窗口边缘: {cursor_pos}")
                return
    
    def on_hover_timer_timeout(self):
        """悬停定时器超时，显示窗口"""
        if self.mouse_hovering and self.is_hidden:
            print(f"鼠标悬停超时，显示窗口")
            self.show_full_window()
    
    def check_mouse_hover_simple(self):
        """简单的鼠标悬停检测 - 只在软件隐藏位置的边缘时触发"""
        if not self.is_hidden:
            print("窗口未隐藏，跳过悬停检测")
            return
        
        # 获取鼠标位置
        cursor_pos = QCursor.pos()
        
        # 获取窗口当前几何信息（隐藏后的位置）
        window_geometry = self.geometry()
        
        # 获取屏幕几何信息
        screen = QApplication.primaryScreen()
        screen_geometry = screen.geometry()
        
        print(f"检查鼠标悬停 - 鼠标位置: {cursor_pos}")
        
        # 检查鼠标是否在软件隐藏位置的边缘
        # 左侧隐藏：检测鼠标是否在窗口右侧5像素区域内
        if window_geometry.x() < 0:  # 左侧隐藏
            edge_area = QRect(window_geometry.x() + window_geometry.width() - 5, 
                             window_geometry.y(), 5, window_geometry.height())
            if edge_area.contains(cursor_pos):
                print(f"检测到鼠标在左侧隐藏窗口边缘，位置: {cursor_pos}")
                self.mouse_hovering = True
                self.hover_timer.start(100)
            else:
                print(f"鼠标不在左侧隐藏窗口边缘: {cursor_pos}")
                self.mouse_hovering = False
                self.hover_timer.stop()
            return
        
        # 右侧隐藏：检测鼠标是否在窗口左侧5像素区域内
        elif window_geometry.x() + window_geometry.width() > screen_geometry.width():  # 右侧隐藏
            edge_area = QRect(window_geometry.x(), 
                             window_geometry.y(), 5, window_geometry.height())
            if edge_area.contains(cursor_pos):
                print(f"检测到鼠标在右侧隐藏窗口边缘，位置: {cursor_pos}")
                self.mouse_hovering = True
                self.hover_timer.start(100)
            else:
                print(f"鼠标不在右侧隐藏窗口边缘: {cursor_pos}")
                self.mouse_hovering = False
                self.hover_timer.stop()
            return
        
        # 顶部隐藏：检测鼠标是否在窗口底部5像素区域内
        elif window_geometry.y() < 0:  # 顶部隐藏
            edge_area = QRect(window_geometry.x(), 
                             window_geometry.y() + window_geometry.height() - 5, 
                             window_geometry.width(), 5)
            if edge_area.contains(cursor_pos):
                print(f"检测到鼠标在顶部隐藏窗口边缘，位置: {cursor_pos}")
                self.mouse_hovering = True
                self.hover_timer.start(100)
            else:
                print(f"鼠标不在顶部隐藏窗口边缘: {cursor_pos}")
                self.mouse_hovering = False
                self.hover_timer.stop()
            return
    
    def check_global_mouse_hover(self):
        """全局鼠标位置检测 - 只在软件隐藏位置的边缘时触发"""
        if not self.is_hidden:
            return
        
        # 获取鼠标位置
        cursor_pos = QCursor.pos()
        
        # 获取窗口当前几何信息（隐藏后的位置）
        window_geometry = self.geometry()
        
        # 获取屏幕几何信息
        screen = QApplication.primaryScreen()
        screen_geometry = screen.geometry()
        
        # 检查鼠标是否在软件隐藏位置的边缘
        # 左侧隐藏：检测鼠标是否在窗口右侧5像素区域内
        if window_geometry.x() < 0:  # 左侧隐藏
            edge_area = QRect(window_geometry.x() + window_geometry.width() - 5, 
                             window_geometry.y(), 5, window_geometry.height())
            if edge_area.contains(cursor_pos):
                print(f"全局检测到鼠标在左侧隐藏窗口边缘，位置: {cursor_pos}")
                self.mouse_hovering = True
                self.hover_timer.start(100)
            else:
                self.mouse_hovering = False
                self.hover_timer.stop()
            return
        
        # 右侧隐藏：检测鼠标是否在窗口左侧5像素区域内
        elif window_geometry.x() + window_geometry.width() > screen_geometry.width():  # 右侧隐藏
            edge_area = QRect(window_geometry.x(), 
                             window_geometry.y(), 5, window_geometry.height())
            if edge_area.contains(cursor_pos):
                print(f"全局检测到鼠标在右侧隐藏窗口边缘，位置: {cursor_pos}")
                self.mouse_hovering = True
                self.hover_timer.start(100)
            else:
                self.mouse_hovering = False
                self.hover_timer.stop()
            return
        
        # 顶部隐藏：检测鼠标是否在窗口底部5像素区域内
        elif window_geometry.y() < 0:  # 顶部隐藏
            edge_area = QRect(window_geometry.x(), 
                             window_geometry.y() + window_geometry.height() - 5, 
                             window_geometry.width(), 5)
            if edge_area.contains(cursor_pos):
                print(f"全局检测到鼠标在顶部隐藏窗口边缘，位置: {cursor_pos}")
                self.mouse_hovering = True
                self.hover_timer.start(100)
            else:
                self.mouse_hovering = False
                self.hover_timer.stop()
            return
    
    def enterEvent(self, event):
        """鼠标进入窗口事件 - 只在软件隐藏位置的边缘时触发"""
        print(f"enterEvent 触发")
        
        # 获取鼠标位置
        cursor_pos = QCursor.pos()
        
        # 获取窗口当前几何信息（隐藏后的位置）
        window_geometry = self.geometry()
        
        # 获取屏幕几何信息
        screen = QApplication.primaryScreen()
        screen_geometry = screen.geometry()
        
        # 检查鼠标是否在软件隐藏位置的边缘
        if self.is_hidden:
            # 左侧隐藏：检测鼠标是否在窗口右侧5像素区域内
            if window_geometry.x() < 0:  # 左侧隐藏
                edge_area = QRect(window_geometry.x() + window_geometry.width() - 5, 
                                 window_geometry.y(), 5, window_geometry.height())
                if edge_area.contains(cursor_pos):
                    # 延迟检测，避免意外触发
                    self.hover_timer.start(100)
                    print(f"鼠标进入左侧隐藏窗口边缘，位置: {cursor_pos}，启动延迟检测")
                    return
            # 右侧隐藏：检测鼠标是否在窗口左侧5像素区域内
            elif window_geometry.x() + window_geometry.width() > screen_geometry.width():  # 右侧隐藏
                edge_area = QRect(window_geometry.x(), 
                                 window_geometry.y(), 5, window_geometry.height())
                if edge_area.contains(cursor_pos):
                    # 延迟检测，避免意外触发
                    self.hover_timer.start(100)
                    print(f"鼠标进入右侧隐藏窗口边缘，位置: {cursor_pos}，启动延迟检测")
                    return
            # 顶部隐藏：检测鼠标是否在窗口底部5像素区域内
            elif window_geometry.y() < 0:  # 顶部隐藏
                edge_area = QRect(window_geometry.x(), 
                                 window_geometry.y() + window_geometry.height() - 5, 
                                 window_geometry.width(), 5)
                if edge_area.contains(cursor_pos):
                    # 延迟检测，避免意外触发
                    self.hover_timer.start(100)
                    print(f"鼠标进入顶部隐藏窗口边缘，位置: {cursor_pos}，启动延迟检测")
                    return
            
            print(f"鼠标未在隐藏窗口边缘，位置: {cursor_pos}，不触发相关操作")
        else:
            # 如果窗口未隐藏，标记为悬停状态，防止自动隐藏
            self.mouse_hovering = True
            self.hover_start_time = QDateTime.currentDateTime()
            print(f"鼠标进入窗口区域，位置: {cursor_pos}，设置悬停状态，防止自动隐藏")
    
    def leaveEvent(self, event):
        """鼠标离开窗口事件"""
        print(f"leaveEvent 触发")
        
        if self.is_hidden:
            # 停止悬停检测
            self.hover_timer.stop()
            self.mouse_hovering = False
            print("鼠标离开隐藏窗口区域，停止检测")
        else:
            # 获取当前鼠标位置
            cursor_pos = QCursor.pos()
            
            # 获取窗口当前位置
            window_geometry = self.geometry()
            window_rect = QRect(window_geometry.x(), window_geometry.y(), 
                              window_geometry.width(), window_geometry.height())
            
            print(f"leaveEvent 检测 - 鼠标位置: {cursor_pos}, 窗口区域: {window_rect}")
            
            # 如果鼠标仍在窗口区域内，不触发自动隐藏检测
            if window_rect.contains(cursor_pos):
                print("鼠标仍在窗口区域内，不触发自动隐藏检测")
                return
            
            print("鼠标确实离开了窗口区域，准备触发自动隐藏检测")
            
            # 如果窗口未隐藏，清除悬停状态
            self.mouse_hovering = False
            self.hover_start_time = None
            
            # 如果用户正在交互，不触发自动隐藏检测
            if not self.user_interacting:
                print("用户未在交互，延迟3秒后检查自动隐藏")
                # 延迟一点时间再检查是否需要隐藏，避免立即隐藏
                QTimer.singleShot(3000, self.check_auto_hide)
            else:
                print("用户正在交互，不触发自动隐藏检测")
    
    def closeEvent(self, event):
        """窗口关闭时保存状态并彻底退出应用程序"""
        print("closeEvent: 开始执行MainWindow关闭事件")
        # 保存窗口状态
        self.saveWindowState()
        
        # 停止所有计时器
        self.saveStateTimer.stop()
        self.mouse_check_timer.stop()
        self.hover_timer.stop()
        self.auto_hide_timer.stop()
        
        # 停止版本检查定时器
        if hasattr(self, 'version_check_timer'):
            self.version_check_timer.stop()
        
        # 停止软件更新检查定时器
        if hasattr(self, 'software_update_timer'):
            self.software_update_timer.stop()
        
        # 停止插件刷新监控定时器
        if hasattr(self, 'plugin_refresh_timer'):
            self.plugin_refresh_timer.stop()
            print("插件刷新监控定时器已停止")
        
        # 关闭AI插件进程 - 必须在版本检查之前执行
        print(f"closeEvent: 检查AI插件进程状态. 进程对象: {getattr(self, 'ai_plugin_process', None)}")
        if hasattr(self, 'ai_plugin_process') and self.ai_plugin_process:
            try:
                print(f"正在关闭AI插件进程 (PID: {self.ai_plugin_process.pid})")
                self.ai_plugin_process.terminate()
                
                # 给进程一些时间来优雅地关闭
                try:
                    self.ai_plugin_process.wait(timeout=5)  # 增加超时时间到5秒
                    print("AI插件进程已优雅关闭")
                except subprocess.TimeoutExpired:
                    print("AI插件进程未在5秒内关闭，强制终止")
                    self.ai_plugin_process.kill()
                    self.ai_plugin_process.wait()
                    print("AI插件进程已强制终止")
                    
            except Exception as e:
                print(f"关闭AI插件进程时出错: {str(e)}")
                try:
                    if self.ai_plugin_process.poll() is None:  # 检查进程是否还在运行
                        self.ai_plugin_process.kill()
                        print("强制终止AI插件进程")
                except:
                    pass
        
        # 检查是否需要关闭时版本比对和上传
        if self.check_version_on_close():
            # 如果用户选择了上传或取消，则不关闭程序
            event.ignore()
            return
        
        # 接受关闭事件
        event.accept()
        
        # 使用计时器延迟调用quit()，确保closeEvent完全处理完毕
        QTimer.singleShot(100, QApplication.instance().quit)

    def check_version_on_close(self):
        """关闭时检查版本号并提示上传"""
        try:
            # 检查是否有WebDAV配置
            settings = QSettings("TodoApp", "WebDAVSync")
            webdav_url = settings.value("url", "")
            webdav_username = settings.value("username", "")
            encrypted_password = settings.value("password", "")
            webdav_remote_path = settings.value("remote_path", "")
            
            # 解密密码
            if encrypted_password:
                from webdav_sync import decrypt_password
                webdav_password = decrypt_password(encrypted_password)
            else:
                webdav_password = ""
            
            # 如果没有配置WebDAV，直接关闭
            if not webdav_url or not webdav_username or not webdav_password:
                print("未配置WebDAV，直接关闭程序")
                return False
            
            # 获取本地版本号
            local_version = self.get_local_version()
            if local_version is None:
                print("无法获取本地版本号，直接关闭程序")
                return False
            
            # 获取远程版本号
            remote_version = self.get_remote_version(webdav_url, webdav_username, webdav_password, webdav_remote_path)
            if remote_version is None:
                # 远程文件不存在，提示是否上传
                return self.show_close_upload_dialog(local_version, None, "首次上传")
            
            # 比较版本号
            if local_version > remote_version:
                # 本地版本更新，提示上传
                return self.show_close_upload_dialog(local_version, remote_version, "上传")
            elif local_version < remote_version:
                # 远程版本更新，提示是否覆盖本地
                return self.show_close_restore_dialog(local_version, remote_version)
            else:
                # 版本号相同，直接关闭
                print(f"版本号一致: {local_version}，直接关闭程序")
                return False
                
        except Exception as e:
            print(f"关闭时版本比对失败: {e}")
            return False

    def show_close_upload_dialog(self, local_version, remote_version, action):
        """显示关闭时上传对话框"""
        from PyQt5.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QMessageBox
        
        dialog = QDialog(self)
        dialog.setWindowTitle("关闭前数据同步")
        dialog.setFixedSize(450, 250)
        dialog.setModal(True)
        
        layout = QVBoxLayout(dialog)
        
        # 版本信息
        if remote_version is None:
            info_text = f"""
检测到本地数据尚未同步到云端：

本地版本: {local_version}
远程版本: 无（首次上传）

建议在关闭前上传数据到云端，确保数据安全。
            """
        else:
            info_text = f"""
检测到本地数据有更新：

本地版本: {local_version}
远程版本: {remote_version}

建议在关闭前上传最新数据到云端。
            """
        
        info_label = QLabel(info_text)
        info_label.setWordWrap(True)
        layout.addWidget(info_label)
        
        # 按钮布局
        button_layout = QHBoxLayout()
        
        # 上传按钮
        upload_button = QPushButton("上传到云端")
        upload_button.clicked.connect(lambda: self.close_upload_to_remote(dialog))
        button_layout.addWidget(upload_button)
        
        # 跳过按钮
        skip_button = QPushButton("跳过并关闭")
        skip_button.clicked.connect(dialog.accept)
        button_layout.addWidget(skip_button)
        
        layout.addLayout(button_layout)
        
        # 显示对话框
        result = dialog.exec_()
        return False  # 无论选择哪个选项，都允许关闭程序

    def show_close_restore_dialog(self, local_version, remote_version):
        """显示关闭时恢复对话框"""
        from PyQt5.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QMessageBox
        
        dialog = QDialog(self)
        dialog.setWindowTitle("关闭前数据同步")
        dialog.setFixedSize(450, 250)
        dialog.setModal(True)
        
        layout = QVBoxLayout(dialog)
        
        # 版本信息
        info_text = f"""
检测到远程数据有更新：

本地版本: {local_version}
远程版本: {remote_version}

建议在关闭前恢复最新数据，或先上传本地数据。
        """
        
        info_label = QLabel(info_text)
        info_label.setWordWrap(True)
        layout.addWidget(info_label)
        
        # 按钮布局
        button_layout = QHBoxLayout()
        
        # 恢复按钮
        restore_button = QPushButton("恢复远程数据")
        restore_button.clicked.connect(lambda: self.restore_from_remote(dialog))
        button_layout.addWidget(restore_button)
        
        # 上传按钮
        upload_button = QPushButton("上传本地数据")
        upload_button.clicked.connect(lambda: self.close_upload_to_remote(dialog))
        button_layout.addWidget(upload_button)
        
        # 跳过按钮
        skip_button = QPushButton("跳过并关闭")
        skip_button.clicked.connect(dialog.accept)
        button_layout.addWidget(skip_button)
        
        layout.addLayout(button_layout)
        
        # 显示对话框
        result = dialog.exec_()
        return False  # 无论选择哪个选项，都允许关闭程序

    def close_upload_to_remote(self, dialog):
        """关闭时上传到远程"""
        try:
            from webdav_sync import WebDAVWorker, decrypt_password
            
            # 显示上传进度提示
            self.show_upload_progress("正在上传数据到云端...")
            
            # 获取WebDAV配置
            settings = QSettings("TodoApp", "WebDAVSync")
            url = settings.value("url", "")
            username = settings.value("username", "")
            encrypted_password = settings.value("password", "")
            remote_path = settings.value("remote_path", "")
            
            # 解密密码
            if encrypted_password:
                password = decrypt_password(encrypted_password)
            else:
                password = ""
            
            # 创建上传线程
            self.close_upload_worker = WebDAVWorker(url, username, password, self.db_file, "upload", remote_path)
            self.close_upload_worker.finished_signal.connect(lambda success, msg: self.on_close_upload_finished(success, msg, dialog))
            self.close_upload_worker.start()
            
        except Exception as e:
            self.hide_upload_progress()  # 隐藏进度提示
            QMessageBox.warning(self, "错误", f"上传失败: {e}")
            dialog.accept()

    def close_restore_from_remote(self, dialog):
        """关闭时从远程恢复"""
        try:
            from webdav_sync import WebDAVWorker, decrypt_password
            
            # 显示恢复进度提示
            self.show_upload_progress("正在从云端恢复数据...")
            
            # 获取WebDAV配置
            settings = QSettings("TodoApp", "WebDAVSync")
            url = settings.value("url", "")
            username = settings.value("username", "")
            encrypted_password = settings.value("password", "")
            remote_path = settings.value("remote_path", "")
            
            # 解密密码
            if encrypted_password:
                password = decrypt_password(encrypted_password)
            else:
                password = ""
            
            # 创建下载线程
            self.close_restore_worker = WebDAVWorker(url, username, password, self.db_file, "download", remote_path)
            self.close_restore_worker.finished_signal.connect(lambda success, msg: self.on_close_restore_finished(success, msg, dialog))
            self.close_restore_worker.start()
            
        except Exception as e:
            self.hide_upload_progress()  # 隐藏进度提示
            QMessageBox.warning(self, "错误", f"恢复失败: {e}")
            dialog.accept()

    def on_close_upload_finished(self, success, message, dialog):
        """关闭时上传完成回调"""
        # 隐藏上传进度提示
        self.hide_upload_progress()
        
        if success:
            QMessageBox.information(self, "成功", "数据上传成功！程序将关闭。")
        else:
            QMessageBox.warning(self, "失败", f"数据上传失败: {message}")
        
        dialog.accept()

    def on_close_restore_finished(self, success, message, dialog):
        """关闭时恢复完成回调"""
        # 隐藏恢复进度提示
        self.hide_upload_progress()
        
        if success:
            QMessageBox.information(self, "成功", "数据恢复成功！程序将关闭。")
            # 重新加载数据
            self.loadData()
        else:
            QMessageBox.warning(self, "失败", f"数据恢复失败: {message}")
        
        dialog.accept()

    # 添加双击显示详情的处理函数
    def showTodoDetail(self, item):
        """显示待办事项详细信息"""
        # 如果窗口处于隐藏状态，先恢复显示
        if self.is_hidden:
            self.show_full_window()
            return
        
        row = self.todo_list_widget.row(item)
        if 0 <= row < len(self.todo_list):
            content, datetime_str, is_important, due_date = self.todo_list[row]
            # 创建并显示详情对话框，传递行索引和列表类型信息
            dialog = TodoDetailDialog(self, content, datetime_str, None, False, row, False, due_date)
            dialog.exec_()
    
    def showCompletedDetail(self, item):
        """显示已完成事项详细信息"""
        # 如果窗口处于隐藏状态，先恢复显示
        if self.is_hidden:
            self.show_full_window()
            return
        
        row = self.completed_list_widget.row(item)
        if 0 <= row < len(self.completed_list):
            content, datetime_str, is_important, completion_datetime, due_date = self.completed_list[row]
            # 创建并显示详情对话框，传递行索引和列表类型信息
            dialog = TodoDetailDialog(self, content, datetime_str, completion_datetime, True, row, True, due_date)
            dialog.exec_()

    # 添加窗口大小改变事件处理，确保列表项更新
    def resizeEvent(self, event):
        """窗口大小改变事件处理"""
        super().resizeEvent(event)
        # 在窗口大小变化后，更新列表项大小
        self.updateListItemSize()
        
        # 重新创建圆角掩码，确保圆角区域透明
        self.createRoundedMask()
        
        # 延迟保存窗口状态，避免频繁保存
        if hasattr(self, 'resize_save_timer'):
            self.resize_save_timer.stop()
        else:
            from PyQt5.QtCore import QTimer
            self.resize_save_timer = QTimer()
            self.resize_save_timer.setSingleShot(True)
            self.resize_save_timer.timeout.connect(self.saveWindowState)
        
        # 500毫秒后保存窗口状态
        self.resize_save_timer.start(500)

    def updateListItemSize(self):
        """更新所有列表项的大小以适应视口宽度变化"""
        # 更新待办列表所有项目
        todo_viewport_width = self.todo_list_widget.viewport().width()
        safe_todo_width = max(200, todo_viewport_width - 15)  # 确保最小宽度
        
        for i in range(self.todo_list_widget.count()):
            item = self.todo_list_widget.item(i)
            widget = self.todo_list_widget.itemWidget(item)
            if widget:
                # 重新计算大小
                widget._truncateTextWithEllipsis()  # 重新截断文本
                size_hint = widget.sizeHint()
                item.setSizeHint(QSize(safe_todo_width, size_hint.height()))
        
        # 更新已完成列表所有项目
        completed_viewport_width = self.completed_list_widget.viewport().width()
        safe_completed_width = max(200, completed_viewport_width - 15)  # 确保最小宽度
        
        for i in range(self.completed_list_widget.count()):
            item = self.completed_list_widget.item(i)
            widget = self.completed_list_widget.itemWidget(item)
            if widget:
                # 重新计算大小
                widget._truncateTextWithEllipsis()  # 重新截断文本
                size_hint = widget.sizeHint()
                item.setSizeHint(QSize(safe_completed_width, size_hint.height()))

    # 添加自启动相关方法
    def toggleAutostart(self, checked):
        """启用或禁用开机自启动，同时控制自动登录"""
        try:
            if checked:
                self.enableAutostart()
                self.autostart_button.setChecked(True)
                # 同时启用自动登录
                self.settings.setValue("auto_login", True)
                print("已启用自动登录")
            else:
                self.disableAutostart()
                self.autostart_button.setChecked(False)
                # 同时禁用自动登录
                self.settings.setValue("auto_login", False)
                print("已禁用自动登录")
        except Exception as e:
            QMessageBox.warning(self, "自启动设置失败", f"无法设置自启动: {str(e)}")
            # 恢复按钮状态
            self.autostart_button.setChecked(self.isAutostartEnabled())
    
    def enableAutostart(self):
        """添加程序到Windows启动项"""
        key = winreg.OpenKey(
            winreg.HKEY_CURRENT_USER,
            r"Software\Microsoft\Windows\CurrentVersion\Run",
            0, winreg.KEY_SET_VALUE
        )
        
        # 获取当前程序路径
        app_path = os.path.abspath(sys.argv[0])
        
        # 如果是.py文件，通过python解释器启动
        if app_path.endswith('.py'):
            execute_path = f'"{sys.executable}" "{app_path}"'
        else:
            # 如果是可执行文件（如.exe），直接启动
            execute_path = f'"{app_path}"'
        
        winreg.SetValueEx(
            key, "TodoApp", 0, winreg.REG_SZ, execute_path
        )
        winreg.CloseKey(key)
        print(f"已启用自启动: {execute_path}")
    
    def disableAutostart(self):
        """从Windows启动项移除程序"""
        try:
            key = winreg.OpenKey(
                winreg.HKEY_CURRENT_USER,
                r"Software\Microsoft\Windows\CurrentVersion\Run",
                0, winreg.KEY_SET_VALUE
            )
            winreg.DeleteValue(key, "TodoApp")
            winreg.CloseKey(key)
            print("已禁用自启动")
        except FileNotFoundError:
            # 如果注册表项不存在，就不需要删除
            print("自启动项不存在")
        except Exception as e:
            print(f"禁用自启动时出错: {e}")
            raise
    
    def isAutostartEnabled(self):
        """检查程序是否已设置为自启动"""
        try:
            key = winreg.OpenKey(
                winreg.HKEY_CURRENT_USER,
                r"Software\Microsoft\Windows\CurrentVersion\Run",
                0, winreg.KEY_READ
            )
            try:
                winreg.QueryValueEx(key, "TodoApp")
                winreg.CloseKey(key)
                return True
            except FileNotFoundError:
                winreg.CloseKey(key)
                return False
        except Exception:
            return False

    def closeAndQuit(self):
        """关闭窗口并彻底退出应用程序"""
        # 保存窗口状态
        self.saveWindowState()
        
        # 停止所有计时器
        self.saveStateTimer.stop()
        
        # 先关闭窗口
        self.close()
        
        # 然后退出应用程序
        QApplication.instance().quit()
        


    # 添加切换列表的方法
    def switchToList(self, index, checked):
        """切换到指定的列表视图
        
        Args:
            index: 要显示的列表索引（0=待办列表，1=已完成列表）
            checked: 按钮是否被选中
        """
        # 阻止按钮被取消选中 - 确保总有一个按钮处于选中状态
        if index == 0:  # 待办列表按钮
            # 如果尝试取消选中待办按钮，则阻止并保持选中状态
            if not checked:
                self.todo_tab_button.setChecked(True)
                return
            # 确保另一个按钮处于未选中状态
            self.completed_tab_button.setChecked(False)
        else:  # 已完成列表按钮
            # 如果尝试取消选中已完成按钮，则阻止并保持选中状态
            if not checked:
                self.completed_tab_button.setChecked(True)
                return
            # 确保另一个按钮处于未选中状态
            self.todo_tab_button.setChecked(False)
        
        # 切换堆叠小部件显示相应列表
        self.stacked_widget.setCurrentIndex(index)
        
        # 更新相应列表内容
        if index == 0:
            self.updateTodoList()
        else:
            self.updateCompletedList()

    # 添加WebDAV云同步设置对话框打开方法
    def open_cloud_settings(self):
        try:
            # 数据库名称已经包含机器码，直接打开WebDAV云设置对话框
            dialog = WebDAVSettingsDialog(self, self.db_file)
            # 连接高度设置改变信号
            dialog.height_setting_changed.connect(self.refreshListsForHeightChange)
            dialog.exec_()
            
        except Exception as e:
            QMessageBox.warning(self, "错误", f"打开WebDAV云设置时出错：{str(e)}")
    
    def check_version_on_startup(self):
        """启动时检查版本号并提示备份或恢复"""
        try:
            # 检查是否有WebDAV配置
            settings = QSettings("TodoApp", "WebDAVSync")
            webdav_url = settings.value("url", "")
            webdav_username = settings.value("username", "")
            encrypted_password = settings.value("password", "")
            webdav_remote_path = settings.value("remote_path", "")
            
            # 解密密码
            if encrypted_password:
                from webdav_sync import decrypt_password
                webdav_password = decrypt_password(encrypted_password)
            else:
                webdav_password = ""
            
            # 如果没有配置WebDAV，跳过检查
            if not webdav_url or not webdav_username or not webdav_password:
                print("未配置WebDAV，跳过版本号比对")
                return
            
            # 获取本地版本号
            local_version = self.get_local_version()
            if local_version is None:
                print("无法获取本地版本号")
                return
            
            # 获取远程版本号
            remote_version = self.get_remote_version(webdav_url, webdav_username, webdav_password, webdav_remote_path)
            if remote_version is None:
                print("无法获取远程版本号，可能是首次使用或网络问题")
                return
            
            # 比较版本号
            if local_version < remote_version:
                # 远程版本更新，提示恢复
                self.show_version_dialog("恢复", local_version, remote_version, "remote")
            elif local_version > remote_version:
                # 本地版本更新，提示备份
                self.show_version_dialog("备份", local_version, remote_version, "local")
            else:
                # 版本号相同，无需操作
                print(f"版本号一致: {local_version}")
                
        except Exception as e:
            print(f"版本号比对失败: {e}")
    
    def get_local_version(self):
        """获取本地数据库版本号"""
        try:
            conn = sqlite3.connect(self.db_file)
            cursor = conn.cursor()
            cursor.execute("SELECT version FROM database_version WHERE id = 1")
            result = cursor.fetchone()
            conn.close()
            return result[0] if result else None
        except Exception as e:
            print(f"获取本地版本号失败: {e}")
            return None
    
    def get_remote_version(self, url, username, password, remote_path):
        """获取远程数据库版本号"""
        try:
            import tempfile
            import requests
            from urllib.parse import urljoin
            
            # 构建远程文件URL - 针对坚果云等WebDAV服务优化
            filename = os.path.basename(self.db_file)
            
            # 检查是否是坚果云URL
            if 'jianguoyun.com' in url:
                # 坚果云需要添加 /dav/ 前缀，但需要处理远程路径中可能已包含的 /dav/ 前缀
                if remote_path:
                    # 如果远程路径已经包含 /dav/ 前缀，则直接使用
                    if remote_path.startswith('/dav/') or remote_path.startswith('dav/'):
                        # 移除开头的 /dav/ 或 dav/，然后重新构建
                        clean_path = remote_path.replace('/dav/', '').replace('dav/', '')
                        remote_url = urljoin(url, f"dav/{clean_path}/{filename}")
                    else:
                        remote_url = urljoin(url, f"dav/{remote_path}/{filename}")
                else:
                    remote_url = urljoin(url, f"dav/todo_backup/{filename}")
            else:
                # 其他WebDAV服务
                if remote_path:
                    remote_url = urljoin(url, f"{remote_path}/{filename}")
                else:
                    remote_url = urljoin(url, f"todo_backup/{filename}")
            
            print(f"尝试访问远程文件: {remote_url}")
            
            # 下载远程数据库文件到临时目录
            auth = (username, password)
            response = requests.get(remote_url, auth=auth, timeout=10)
            
            if response.status_code != 200:
                print(f"远程文件不存在或无法访问: {response.status_code}")
                print(f"响应内容: {response.text[:200]}")  # 显示前200个字符
                return None
            
            # 保存到临时文件
            with tempfile.NamedTemporaryFile(delete=False, suffix='.db') as temp_file:
                temp_file.write(response.content)
                temp_file_path = temp_file.name
            
            # 读取临时文件的版本号
            conn = sqlite3.connect(temp_file_path)
            cursor = conn.cursor()
            
            # 检查是否存在database_version表
            cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='database_version'")
            if not cursor.fetchone():
                print("远程数据库中没有database_version表，可能是旧版本数据库")
                print("将远程版本号设为1（兼容旧版本）")
                conn.close()
                os.unlink(temp_file_path)
                return 1  # 旧版本数据库，版本号设为1
            
            # 获取版本号
            cursor.execute("SELECT version FROM database_version WHERE id = 1")
            result = cursor.fetchone()
            conn.close()
            
            # 删除临时文件
            os.unlink(temp_file_path)
            
            if result:
                print(f"成功获取远程版本号: {result[0]}")
                return result[0]
            else:
                print("远程数据库中没有找到版本号信息")
                return None
            
        except Exception as e:
            print(f"获取远程版本号失败: {e}")
            return None
    
    def show_version_dialog(self, action, local_version, remote_version, source):
        """显示版本比对对话框"""
        from PyQt5.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QMessageBox
        
        dialog = QDialog(self)
        dialog.setWindowTitle("版本比对提示")
        dialog.setFixedSize(400, 200)
        
        layout = QVBoxLayout(dialog)
        
        # 版本信息
        info_text = f"""
检测到版本差异：

本地版本: {local_version}
远程版本: {remote_version}

建议操作: {action}数据
        """
        
        info_label = QLabel(info_text)
        info_label.setWordWrap(True)
        layout.addWidget(info_label)
        
        # 按钮布局
        button_layout = QHBoxLayout()
        
        if action == "恢复":
            # 恢复按钮
            restore_button = QPushButton("恢复远程数据")
            restore_button.clicked.connect(lambda: self.restore_from_remote(dialog))
            button_layout.addWidget(restore_button)
            
            # 跳过按钮
            skip_button = QPushButton("跳过")
            skip_button.clicked.connect(dialog.reject)
            button_layout.addWidget(skip_button)
            
        else:  # 备份
            # 备份按钮
            backup_button = QPushButton("备份到远程")
            backup_button.clicked.connect(lambda: self.backup_to_remote(dialog))
            button_layout.addWidget(backup_button)
            
            # 跳过按钮
            skip_button = QPushButton("跳过")
            skip_button.clicked.connect(dialog.reject)
            button_layout.addWidget(skip_button)
        
        layout.addLayout(button_layout)
        
        # 显示对话框
        dialog.exec_()
    
    def restore_from_remote(self, dialog):
        """从远程恢复数据"""
        try:
            from webdav_sync import WebDAVWorker, decrypt_password
            from PyQt5.QtCore import QThread
            
            # 显示恢复进度提示
            self.show_upload_progress("正在从云端恢复数据...")
            
            # 获取WebDAV配置
            settings = QSettings("TodoApp", "WebDAVSync")
            url = settings.value("url", "")
            username = settings.value("username", "")
            encrypted_password = settings.value("password", "")
            remote_path = settings.value("remote_path", "")
            
            # 解密密码
            if encrypted_password:
                password = decrypt_password(encrypted_password)
            else:
                password = ""
            
            # 创建下载线程
            self.restore_worker = WebDAVWorker(url, username, password, self.db_file, "download", remote_path)
            self.restore_worker.finished_signal.connect(lambda success, msg: self.on_restore_finished(success, msg, dialog))
            self.restore_worker.start()
            
        except Exception as e:
            self.hide_upload_progress()  # 隐藏进度提示
            QMessageBox.warning(self, "错误", f"恢复失败: {e}")
            dialog.reject()
    
    def backup_to_remote(self, dialog):
        """备份数据到远程"""
        try:
            from webdav_sync import WebDAVWorker, decrypt_password
            from PyQt5.QtCore import QThread
            
            # 显示备份进度提示
            self.show_upload_progress("正在备份数据到云端...")
            
            # 获取WebDAV配置
            settings = QSettings("TodoApp", "WebDAVSync")
            url = settings.value("url", "")
            username = settings.value("username", "")
            encrypted_password = settings.value("password", "")
            remote_path = settings.value("remote_path", "")
            
            # 解密密码
            if encrypted_password:
                password = decrypt_password(encrypted_password)
            else:
                password = ""
            
            # 创建上传线程
            self.backup_worker = WebDAVWorker(url, username, password, self.db_file, "upload", remote_path)
            self.backup_worker.finished_signal.connect(lambda success, msg: self.on_backup_finished(success, msg, dialog))
            self.backup_worker.start()
            
        except Exception as e:
            self.hide_upload_progress()  # 隐藏进度提示
            QMessageBox.warning(self, "错误", f"备份失败: {e}")
            dialog.reject()
    
    def on_restore_finished(self, success, message, dialog):
        """恢复完成回调"""
        # 隐藏恢复进度提示
        self.hide_upload_progress()
        
        if success:
            QMessageBox.information(self, "成功", "数据恢复成功！")
            # 重新加载数据
            self.loadData()
        else:
            QMessageBox.warning(self, "失败", f"数据恢复失败: {message}")
        
        dialog.accept()
    
    def on_backup_finished(self, success, message, dialog):
        """备份完成回调"""
        # 隐藏备份进度提示
        self.hide_upload_progress()
        
        if success:
            QMessageBox.information(self, "成功", "数据备份成功！")
        else:
            QMessageBox.warning(self, "失败", f"数据备份失败: {message}")
        
        dialog.accept()

    def reset_original_geometry(self):
        """重置原始几何信息"""
        self.original_geometry = None
        print("已重置原始几何信息")
    
    def toggleImportant(self, index):
        """切换待办事项的重要标记状态"""
        if 0 <= index < len(self.todo_list):
            # 在数据库中更新状态
            conn = sqlite3.connect(self.db_file)
            cursor = conn.cursor()
            
            # 获取待办事项内容和日期时间
            content, datetime_str, current_important, due_date = self.todo_list[index]
            
            # 切换重要标记状态
            new_important = not current_important
            
            # 更新重要标记
            cursor.execute(
                "UPDATE todos SET important = ? WHERE content = ? AND datetime = ? AND completed = 0", 
                (1 if new_important else 0, content, datetime_str)
            )
            
            # 更新全局版本号
            current_time = QDateTime.currentDateTime().toString("yyyy-MM-dd HH:mm:ss")
            cursor.execute(
                "UPDATE database_version SET version = version + 1, last_updated = ? WHERE id = 1",
                (current_time,)
            )
            
            conn.commit()
            conn.close()
            
            # 刷新列表（重要事项会自动置顶）
            self.loadData()
            
            # 延迟重置用户交互状态，允许自动隐藏
            QTimer.singleShot(1000, lambda: setattr(self, 'user_interacting', False))
            
            # 自动上传到远程服务器
            if self.auto_upload_enabled:
                self.auto_upload_to_remote()
    
    def restoreTodo(self, index):
        if 0 <= index < len(self.completed_list):
            # 在数据库中更新状态
            conn = sqlite3.connect(self.db_file)
            cursor = conn.cursor()
            
            # 获取已完成事项内容和日期时间
            content, datetime_str, is_important, completion_datetime, due_date = self.completed_list[index]
            
            # 更新为未完成，并清除完成时间
            cursor.execute(
                "UPDATE todos SET completed = 0, reminder_time = NULL WHERE content = ? AND datetime = ? AND completed = 1", 
                (content, datetime_str)
            )
            
            # 更新全局版本号
            current_time = QDateTime.currentDateTime().toString("yyyy-MM-dd HH:mm:ss")
            cursor.execute(
                "UPDATE database_version SET version = version + 1, last_updated = ? WHERE id = 1",
                (current_time,)
            )
            
            conn.commit()
            conn.close()
            
            # 刷新列表（重要事项会自动置顶）
            self.loadData()
            
            # 自动上传到远程服务器
            if self.auto_upload_enabled:
                self.auto_upload_to_remote()
    

    

    

    

    

    
    def loadData(self):
        try:
            # 从数据库加载数据
            conn = sqlite3.connect(self.db_file)
            cursor = conn.cursor()
            
            # 获取未完成的待办事项（加上日期时间、重要标记和完成时间）
            # 重要事项优先显示，然后按时间降序排列
            cursor.execute("""
                SELECT content, datetime, important, due_date FROM todos 
                WHERE completed = 0 
                ORDER BY important DESC, datetime DESC
            """)
            self.todo_list = [(row[0], row[1], bool(row[2]), row[3]) for row in cursor.fetchall()]
            print(f"从数据库加载了 {len(self.todo_list)} 条待办事项")
            
            # 获取已完成的事项（加上日期时间、重要标记、完成时间和截止时间）
            # 按完成时间降序排列，最近完成的排在上面
            cursor.execute("""
                SELECT content, datetime, important, completion_datetime, due_date FROM todos 
                WHERE completed = 1 
                ORDER BY completion_datetime DESC
            """)
            self.completed_list = [(row[0], row[1], bool(row[2]), row[3], row[4]) for row in cursor.fetchall()]
            print(f"从数据库加载了 {len(self.completed_list)} 条已完成事项")
            
            conn.close()
            
            # 更新列表显示
            self.updateTodoList()
            self.updateCompletedList()
            
            # 保持当前选中的列表视图状态不变
            # 不需要修改按钮状态，因为按钮状态与堆叠窗口状态已经绑定
            
        except sqlite3.Error as e:
            error_msg = f"数据库错误: {str(e)}"
            print(error_msg)
            QMessageBox.warning(self, "加载失败", error_msg)
        except Exception as e:
            error_msg = f"加载数据时发生未知错误: {str(e)}"
            print(error_msg)
            QMessageBox.warning(self, "加载失败", error_msg)
    
    def saveWindowState(self):
        """保存窗口位置、大小和状态"""
        self.settings.setValue("geometry", self.geometry())
        self.settings.setValue("is_interface_locked", self.is_interface_locked)
        # 保存当前显示的列表索引
        self.settings.setValue("current_list_index", self.stacked_widget.currentIndex())
        print("已保存窗口状态")
    
    def restoreWindowState(self):
        """恢复窗口位置、大小和状态"""
        # 恢复窗口位置和大小
        if self.settings.contains("geometry"):
            geometry = self.settings.value("geometry")
            if isinstance(geometry, QRect):  # QRect对象
                self.setGeometry(geometry)
            print("已恢复窗口位置和大小")
        
        # 恢复锁定状态
        if self.settings.contains("is_interface_locked"):
            is_interface_locked = self.settings.value("is_interface_locked")
            # 将字符串'true'/'false'转换为布尔值
            if isinstance(is_interface_locked, str):
                is_interface_locked = (is_interface_locked.lower() == 'true')
            elif not isinstance(is_interface_locked, bool):
                is_interface_locked = bool(is_interface_locked)
                
            print(f"恢复桌面锁定状态: {is_interface_locked}")
            
            if is_interface_locked:
                self.lock_button.setChecked(True)
                # 直接设置锁定状态
                self.is_interface_locked = True
                self.update_lock_button_icon()
                # 停止自动隐藏定时器，防止锁定后界面被隐藏
                self.auto_hide_timer.stop()
                # 应用桌面锁定设置
                self.setWindowFlags(Qt.FramelessWindowHint | Qt.Tool)
                self.setAttribute(Qt.WA_ShowWithoutActivating, True)
                self.lower()  # 移动到桌面底层
                self.show()  # 需要重新显示窗口以应用标志
                

        
        # 恢复当前显示的列表
        if self.settings.contains("current_list_index"):
            list_index = self.settings.value("current_list_index")
            # 如果是字符串，转换为整数
            if isinstance(list_index, str):
                list_index = int(list_index)
            elif not isinstance(list_index, int):
                list_index = int(list_index)
            
            # 设置对应的显示状态
            if list_index == 1:  # 已完成列表
                self.stacked_widget.setCurrentIndex(1)
                self.todo_tab_button.setChecked(False)
                self.completed_tab_button.setChecked(True)
            else:  # 默认或无效索引，显示待办列表
                self.stacked_widget.setCurrentIndex(0)
                self.todo_tab_button.setChecked(True)
                self.completed_tab_button.setChecked(False)

    # 完全重写窗口鼠标事件处理函数，简化调整大小的逻辑
    def mousePressEvent(self, event):
        """鼠标按下事件处理"""
        if event.button() == Qt.LeftButton:
            # 设置用户交互标志，防止自动隐藏
            self.user_interacting = True
            
            # 如果锁定到桌面，只允许互动元素响应
            if self.is_interface_locked:
                # 检查是否点击的是互动元素（按钮、输入框、列表等）
                clicked_widget = self.childAt(event.pos())
                if clicked_widget and self.is_interactive_widget(clicked_widget):
                    # 允许互动元素响应
                    super().mousePressEvent(event)
                else:
                    # 非互动元素，阻止移动和调整大小
                    event.accept()
                    return
            else:
                # 未锁定时，正常处理
                # 检查是否在标题栏区域
                if event.y() < 30:  # 标题栏高度
                    self.is_dragging = True
                    self.drag_position = event.globalPos() - self.frameGeometry().topLeft()
                    event.accept()
                else:
                    # 检查是否在调整大小区域
                    window_rect = self.rect()
                    resize_margin = 5
                    
                    # 检查是否在边缘区域
                    if (event.x() <= resize_margin or 
                        event.x() >= window_rect.width() - resize_margin or
                        event.y() <= resize_margin or 
                        event.y() >= window_rect.height() - resize_margin):
                        
                        self.resizing = True
                        self.resize_start_position = event.globalPos()
                        self.start_geometry = self.geometry()
                        event.accept()
                    else:
                        # 普通点击，传递给父类处理
                        super().mousePressEvent(event)
        else:
            super().mousePressEvent(event)

    def mouseMoveEvent(self, event):
        """鼠标移动事件处理"""
        # 如果锁定到桌面，阻止所有移动和调整大小操作
        if self.is_interface_locked:
            # 只允许互动元素内的鼠标移动
            hovered_widget = self.childAt(event.pos())
            if hovered_widget and self.is_interactive_widget(hovered_widget):
                # 互动元素内，设置正常光标
                self.setCursor(Qt.ArrowCursor)
            else:
                # 非互动元素，设置禁止光标
                # 使用临时变量存储当前位置，避免光标状态持续存在
                self.setCursor(Qt.ForbiddenCursor)
            
            # 阻止事件传递，避免其他处理逻辑
            event.accept()
            return
        
        # 如果窗口处于隐藏状态，检查鼠标悬停
        if self.is_hidden:
            self.check_global_mouse_hover()
        
        if event.buttons() & Qt.LeftButton:
            if self.resizing:
                # 计算大小增量
                if self.resize_start_position is not None and self.start_geometry is not None:
                    delta = event.globalPos() - self.resize_start_position
                    new_width = max(self.minimumWidth(), self.start_geometry.width() + delta.x())
                    new_height = max(self.minimumHeight(), self.start_geometry.height() + delta.y())
                    self.resize(new_width, new_height)
            else:
                # 移动窗口
                if self.drag_position is not None:
                    self.move(event.globalPos() - self.drag_position)
        else:
            # 检查鼠标是否在右下角
            rect = self.rect()
            bottomRight = rect.bottomRight()
            resizeArea = QRect(bottomRight.x() - 20, bottomRight.y() - 20, 20, 20)
            
            if not self.is_interface_locked and resizeArea.contains(event.pos()):
                self.setCursor(Qt.SizeFDiagCursor)
            else:
                self.setCursor(Qt.ArrowCursor)

    def mouseReleaseEvent(self, event):
        """鼠标释放事件"""
        # 如果锁定到桌面，检查是否在交互式控件上释放鼠标
        if self.is_interface_locked:
            # 检查是否点击的是互动元素
            released_widget = self.childAt(event.pos())
            if released_widget and self.is_interactive_widget(released_widget):
                # 允许互动元素响应
                super().mouseReleaseEvent(event)
            else:
                # 非互动元素，阻止事件
                event.accept()
            
            # 重置为默认光标
            self.setCursor(Qt.ArrowCursor)
            return
            
        # 非锁定状态下的正常处理
        if event.button() == Qt.LeftButton:
            if self.is_dragging:
                self.is_dragging = False
                self.drag_position = None
                # 延迟清除用户交互标志，给右键菜单足够的时间
                QTimer.singleShot(500, lambda: setattr(self, 'user_interacting', False))
                event.accept()
            elif self.resizing:
                self.resizing = False
                self.resize_start_position = None
                self.start_geometry = None
                # 延迟清除用户交互标志，给右键菜单足够的时间
                QTimer.singleShot(500, lambda: setattr(self, 'user_interacting', False))
                event.accept()
            else:
                # 普通点击，延迟清除用户交互标志
                QTimer.singleShot(500, lambda: setattr(self, 'user_interacting', False))
                super().mouseReleaseEvent(event)
        else:
            super().mouseReleaseEvent(event)
    
    def check_auto_hide(self):
        """检查是否需要自动隐藏"""
        if self.is_interface_locked:
            print("桌面已锁定，跳过自动隐藏检测")
            return
        
        # 如果正在执行动画，跳过检测
        if ((self.hide_animation and self.hide_animation.state() == QPropertyAnimation.Running) or
            (self.show_animation and self.show_animation.state() == QPropertyAnimation.Running)):
            print("正在执行动画，跳过自动隐藏检测")
            return
        
        # 如果鼠标正在悬停，不允许隐藏
        if self.mouse_hovering:
            print("鼠标正在悬停，不允许隐藏")
            return
        
        # 如果用户正在与窗口交互，不允许隐藏
        if self.user_interacting:
            print("用户正在交互，不允许隐藏")
            return
        
        # 移除延迟检测，让窗口立即隐藏
        # 如果窗口刚显示不久，延迟检测，避免立即隐藏
        # if hasattr(self, 'last_show_time'):
        #     current_time = QDateTime.currentDateTime()
        #     time_diff = self.last_show_time.msecsTo(current_time)
        #     if time_diff < 2000:  # 2秒内不自动隐藏
        #         print(f"窗口刚显示 {time_diff}ms，延迟检测")
        #         return
        
        # 获取当前鼠标位置
        cursor_pos = QCursor.pos()
        
        # 获取窗口当前位置
        window_geometry = self.geometry()
        window_x = window_geometry.x()
        window_y = window_geometry.y()
        window_width = window_geometry.width()
        window_height = window_geometry.height()
        
        # 检查鼠标是否在窗口区域内（包括边缘区域）
        window_rect = QRect(window_x, window_y, window_width, window_height)
        
        print(f"自动隐藏检测 - 鼠标位置: {cursor_pos}, 窗口区域: {window_rect}")
        
        # 如果鼠标仍在窗口区域内，不隐藏
        if window_rect.contains(cursor_pos):
            print("鼠标仍在窗口区域内，不隐藏")
            return
        
        # 额外检查：如果鼠标在窗口附近（扩大检测区域），也不隐藏
        expanded_rect = QRect(window_x - 10, window_y - 10, window_width + 20, window_height + 20)
        if expanded_rect.contains(cursor_pos):
            print("鼠标在窗口附近区域，不隐藏")
            return
        
        print("鼠标已离开窗口区域，继续检查隐藏条件")
        
        # 获取屏幕几何信息
        screen = QApplication.primaryScreen()
        screen_geometry = screen.geometry()
        
        # 检查是否靠近左边缘
        if window_x <= self.auto_hide_threshold and not self.is_hidden:
            print(f"窗口靠近左边缘 (x={window_x}, threshold={self.auto_hide_threshold})")
            # 检查鼠标是否在隐藏区域附近，如果是则不隐藏
            hide_area = QRect(0, window_y, 50, window_height)  # 左侧50像素区域
            if not hide_area.contains(cursor_pos):
                print("鼠标不在左侧隐藏区域，执行隐藏到左侧")
                self.hide_to_left()
            else:
                print("鼠标在左侧隐藏区域，不隐藏")
        # 检查是否靠近右边缘
        elif (window_x + window_width) >= (screen_geometry.width() - self.auto_hide_threshold) and not self.is_hidden:
            print(f"窗口靠近右边缘 (x={window_x}, width={window_width}, screen_width={screen_geometry.width()})")
            # 检查鼠标是否在隐藏区域附近，如果是则不隐藏
            hide_area = QRect(screen_geometry.width() - 50, window_y, 50, window_height)  # 右侧50像素区域
            if not hide_area.contains(cursor_pos):
                print("鼠标不在右侧隐藏区域，执行隐藏到右侧")
                self.hide_to_right()
            else:
                print("鼠标在右侧隐藏区域，不隐藏")
        # 检查是否靠近顶部边缘
        elif window_y <= self.auto_hide_threshold and not self.is_hidden:
            print(f"窗口靠近顶部边缘 (y={window_y}, threshold={self.auto_hide_threshold})")
            # 检查鼠标是否在隐藏区域附近，如果是则不隐藏
            hide_area = QRect(window_x, 0, window_width, 50)  # 顶部50像素区域
            if not hide_area.contains(cursor_pos):
                print("鼠标不在顶部隐藏区域，执行隐藏到顶部")
                self.hide_to_top()
            else:
                print("鼠标在顶部隐藏区域，不隐藏")
        # 检查是否应该恢复显示
        elif self.is_hidden:
            print("窗口已隐藏，检查是否应该恢复显示")
            # 检查是否远离所有边缘
            away_from_left = window_x > self.auto_hide_threshold
            away_from_right = (window_x + window_width) < (screen_geometry.width() - self.auto_hide_threshold)
            away_from_top = window_y > self.auto_hide_threshold
            
            if away_from_left and away_from_right and away_from_top:
                print("窗口远离所有边缘，恢复显示")
                self.show_full_window()
            else:
                print("窗口仍在边缘，不恢复显示")
        else:
            print("窗口不在任何边缘，不执行隐藏")
    
    def hide_to_left(self):
        """隐藏到左侧"""
        print(f"尝试隐藏到左侧 - is_hidden: {self.is_hidden}")
        
        if self.is_hidden or (self.hide_animation and self.hide_animation.state() == QPropertyAnimation.Running):
            print("隐藏条件不满足，跳过隐藏")
            return
        
        print("开始隐藏到左侧")
        
        # 设置隐藏状态
        self.is_hidden = True
        
        # 隐藏到任务栏 - 设置窗口标志使图标不显示在任务栏，并置顶
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool)
        self.show()
        
        # 隐藏到托盘
        self.hide_to_tray()
        
        # 创建动画
        self.hide_animation = QPropertyAnimation(self, b"geometry")
        self.hide_animation.setDuration(self.animation_duration)
        self.hide_animation.setEasingCurve(QEasingCurve.OutQuart)  # 使用更平滑的缓动曲线
        self.hide_animation.setLoopCount(1)
                
        # 设置动画的起始和结束状态
        start_geometry = self.geometry()
        window_width = self.width()
        window_height = self.height()
        
        # 隐藏到左侧，只露出5像素
        end_geometry = QRect(-window_width + 5, start_geometry.y(), window_width, window_height)
        
        print(f"隐藏动画起始位置: {start_geometry}")
        print(f"隐藏动画结束位置: {end_geometry}")
        
        self.hide_animation.setStartValue(start_geometry)
        self.hide_animation.setEndValue(end_geometry)
        
        # 连接动画完成信号
        self.hide_animation.finished.connect(self.on_hide_animation_finished)
        
        # 开始动画
        self.hide_animation.start()
        
        print("窗口开始平滑隐藏到左侧")
    
    def on_hide_animation_finished(self):
        """隐藏动画完成后的回调"""
        print("窗口已自动隐藏到左侧")
    
    def hide_to_right(self):
        """隐藏到右侧"""
        if self.is_hidden or (self.hide_animation and self.hide_animation.state() == QPropertyAnimation.Running):
            return
        
        # 设置隐藏状态
        self.is_hidden = True
        
        # 隐藏到任务栏 - 设置窗口标志使图标不显示在任务栏，并置顶
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool)
        self.show()
        
        # 隐藏到托盘
        self.hide_to_tray()
        
        # 获取屏幕宽度
        screen = QApplication.primaryScreen()
        screen_width = screen.geometry().width()
        
        # 创建动画
        self.hide_animation = QPropertyAnimation(self, b"geometry")
        self.hide_animation.setDuration(self.animation_duration)
        self.hide_animation.setEasingCurve(QEasingCurve.OutQuart)  # 使用更平滑的缓动曲线
        self.hide_animation.setLoopCount(1)
                
        # 设置动画的起始和结束状态
        start_geometry = self.geometry()
        window_width = self.width()
        window_height = self.height()
        
        # 隐藏到右侧，只露出5像素
        end_geometry = QRect(screen_width - 5, start_geometry.y(), window_width, window_height)
        
        self.hide_animation.setStartValue(start_geometry)
        self.hide_animation.setEndValue(end_geometry)
        
        # 连接动画完成信号
        self.hide_animation.finished.connect(self.on_hide_animation_finished)
        
        # 开始动画
        self.hide_animation.start()
        
        print("窗口开始平滑隐藏到右侧")
    
    def hide_to_top(self):
        """隐藏到顶部"""
        if self.is_hidden or (self.hide_animation and self.hide_animation.state() == QPropertyAnimation.Running):
            return
        
        # 设置隐藏状态
        self.is_hidden = True
        
        # 隐藏到任务栏 - 设置窗口标志使图标不显示在任务栏，并置顶
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool)
        self.show()
        
        # 创建动画
        self.hide_animation = QPropertyAnimation(self, b"geometry")
        self.hide_animation.setDuration(self.animation_duration)
        self.hide_animation.setEasingCurve(QEasingCurve.OutQuart)  # 使用更平滑的缓动曲线
        self.hide_animation.setLoopCount(1)
                
        # 设置动画的起始和结束状态
        start_geometry = self.geometry()
        window_width = self.width()
        window_height = self.height()
        
        # 隐藏到顶部，只露出5像素
        end_geometry = QRect(start_geometry.x(), -window_height + 5, window_width, window_height)
        
        self.hide_animation.setStartValue(start_geometry)
        self.hide_animation.setEndValue(end_geometry)
        
        # 连接动画完成信号
        self.hide_animation.finished.connect(self.on_hide_animation_finished)
        
        # 开始动画
        self.hide_animation.start()
        
        print("窗口开始平滑隐藏到顶部")
    
    def show_full_window(self):
        """恢复显示完整窗口"""
        print(f"尝试显示完整窗口 - is_hidden: {self.is_hidden}")
        
        if not self.is_hidden or (self.show_animation and self.show_animation.state() == QPropertyAnimation.Running):
            print("显示条件不满足，跳过显示")
            return
        
        print("开始显示完整窗口")
        
        # 重置隐藏状态
        self.is_hidden = False
        
        # 先设置窗口标志，避免闪烁，始终隐藏任务栏图标
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool)
        self.setAttribute(Qt.WA_ShowWithoutActivating, False)  # 确保窗口可以激活
        
        # 获取屏幕几何信息
        screen = QApplication.primaryScreen()
        screen_geometry = screen.geometry()
        
        # 从窗口的实际大小获取尺寸
        window_width = self.width()
        window_height = self.height()
        
        # 如果当前窗口尺寸太小，使用最小尺寸
        if window_width < self.minimumSize().width():
            window_width = self.minimumSize().width()
        if window_height < self.minimumSize().height():
            window_height = self.minimumSize().height()
        
        # 根据当前窗口位置判断从哪个边缘弹出
        current_x = self.x()
        current_y = self.y()
        
        # 如果窗口在左侧边缘，从左侧弹出
        if current_x <= 0:
            x = 0
            y = current_y
        # 如果窗口在右侧边缘，从右侧弹出
        elif current_x + window_width >= screen_geometry.width():
            x = screen_geometry.width() - window_width
            y = current_y
        # 如果窗口在顶部边缘，从顶部弹出
        elif current_y <= 0:
            x = current_x
            y = 0
        # 否则使用当前位置
        else:
            x = current_x
            y = current_y
        
        # 确保窗口不会超出屏幕边界
        if x < 0:
            x = 0
        if y < 0:
            y = 0
        if x + window_width > screen_geometry.width():
            x = screen_geometry.width() - window_width
        if y + window_height > screen_geometry.height():
            y = screen_geometry.height() - window_height
        
        end_geometry = QRect(x, y, window_width, window_height)
        
        print(f"最终恢复位置: {end_geometry}")
        
        # 创建显示动画
        self.show_animation = QPropertyAnimation(self, b"geometry")
        self.show_animation.setDuration(self.animation_duration)
        self.show_animation.setEasingCurve(QEasingCurve.OutQuart)  # 使用更平滑的缓动曲线
        self.show_animation.setLoopCount(1)
                
        # 设置动画的起始和结束状态
        # 使用当前隐藏位置作为起始位置，确保动画从隐藏位置开始
        current_geometry = self.geometry()
        print(f"显示动画起始位置: {current_geometry}")
        print(f"显示动画结束位置: {end_geometry}")
        
        self.show_animation.setStartValue(current_geometry)
        self.show_animation.setEndValue(end_geometry)
        
        # 连接动画完成信号
        self.show_animation.finished.connect(self.on_show_animation_finished)
        
        # 开始动画
        self.show_animation.start()
        
        print("窗口开始平滑显示")
        
        # 清除悬停状态，停止悬停检测
        self.mouse_hovering = False
        self.hover_start_time = None
        self.hover_timer.stop()
        
        # 记录窗口显示时间，防止立即隐藏
        self.last_show_time = QDateTime.currentDateTime()
    
    def on_show_animation_finished(self):
        """显示动画完成后的回调"""
        print("窗口已恢复显示")
        # 确保窗口可见并置顶
        self.show()
        self.raise_()
        self.activateWindow()
        self.setFocus()  # 确保窗口获得焦点
    
    def check_mouse_hover(self):
        """检查鼠标悬停 - 只在软件隐藏位置的边缘时触发"""
        if not self.is_hidden:
            return
        
        # 获取鼠标位置
        cursor_pos = QCursor.pos()
        
        # 获取窗口当前几何信息（隐藏后的位置）
        window_geometry = self.geometry()
        
        # 获取屏幕几何信息
        screen = QApplication.primaryScreen()
        screen_geometry = screen.geometry()
        
        # 检查鼠标是否在软件隐藏位置的边缘
        # 左侧隐藏：检测鼠标是否在窗口右侧5像素区域内
        if window_geometry.x() < 0:  # 左侧隐藏
            edge_area = QRect(window_geometry.x() + window_geometry.width() - 5, 
                             window_geometry.y(), 5, window_geometry.height())
            if edge_area.contains(cursor_pos):
                if not self.mouse_hovering:
                    self.mouse_hovering = True
                    self.hover_start_time = QDateTime.currentDateTime()
                    print(f"检测到鼠标在左侧隐藏窗口边缘，位置: {cursor_pos}, 启动悬停检测")
                    self.hover_timer.start(100)
                return
            else:
                if self.mouse_hovering:
                    self.mouse_hovering = False
                    self.hover_start_time = None
                    self.hover_timer.stop()
                    print(f"鼠标离开左侧隐藏窗口边缘: {cursor_pos}")
                return
        
        # 右侧隐藏：检测鼠标是否在窗口左侧5像素区域内
        elif window_geometry.x() + window_geometry.width() > screen_geometry.width():  # 右侧隐藏
            edge_area = QRect(window_geometry.x(), 
                             window_geometry.y(), 5, window_geometry.height())
            if edge_area.contains(cursor_pos):
                if not self.mouse_hovering:
                    self.mouse_hovering = True
                    self.hover_start_time = QDateTime.currentDateTime()
                    print(f"检测到鼠标在右侧隐藏窗口边缘，位置: {cursor_pos}, 启动悬停检测")
                    self.hover_timer.start(100)
                return
            else:
                if self.mouse_hovering:
                    self.mouse_hovering = False
                    self.hover_start_time = None
                    self.hover_timer.stop()
                    print(f"鼠标离开右侧隐藏窗口边缘: {cursor_pos}")
                return
        
        # 顶部隐藏：检测鼠标是否在窗口底部5像素区域内
        elif window_geometry.y() < 0:  # 顶部隐藏
            edge_area = QRect(window_geometry.x(), 
                             window_geometry.y() + window_geometry.height() - 5, 
                             window_geometry.width(), 5)
            if edge_area.contains(cursor_pos):
                if not self.mouse_hovering:
                    self.mouse_hovering = True
                    self.hover_start_time = QDateTime.currentDateTime()
                    print(f"检测到鼠标在顶部隐藏窗口边缘，位置: {cursor_pos}, 启动悬停检测")
                    self.hover_timer.start(100)
                return
            else:
                if self.mouse_hovering:
                    self.mouse_hovering = False
                    self.hover_start_time = None
                    self.hover_timer.stop()
                    print(f"鼠标离开顶部隐藏窗口边缘: {cursor_pos}")
                return
    
    def on_hover_timer_timeout(self):
        """悬停定时器超时，显示窗口"""
        if self.mouse_hovering and self.is_hidden:
            print(f"鼠标悬停超时，显示窗口")
            self.show_full_window()
    
    def check_mouse_hover_simple(self):
        """简单的鼠标悬停检测 - 只在软件隐藏位置的边缘时触发"""
        if not self.is_hidden:
            print("窗口未隐藏，跳过悬停检测")
            return
        
        # 获取鼠标位置
        cursor_pos = QCursor.pos()
        
        # 获取窗口当前几何信息（隐藏后的位置）
        window_geometry = self.geometry()
        
        # 获取屏幕几何信息
        screen = QApplication.primaryScreen()
        screen_geometry = screen.geometry()
        
        print(f"检查鼠标悬停 - 鼠标位置: {cursor_pos}")
        
        # 检查鼠标是否在软件隐藏位置的边缘
        # 左侧隐藏：检测鼠标是否在窗口右侧5像素区域内
        if window_geometry.x() < 0:  # 左侧隐藏
            edge_area = QRect(window_geometry.x() + window_geometry.width() - 5, 
                             window_geometry.y(), 5, window_geometry.height())
            if edge_area.contains(cursor_pos):
                print(f"检测到鼠标在左侧隐藏窗口边缘，位置: {cursor_pos}")
                self.mouse_hovering = True
                self.hover_timer.start(100)
            else:
                print(f"鼠标不在左侧隐藏窗口边缘: {cursor_pos}")
                self.mouse_hovering = False
                self.hover_timer.stop()
            return
        
        # 右侧隐藏：检测鼠标是否在窗口左侧5像素区域内
        elif window_geometry.x() + window_geometry.width() > screen_geometry.width():  # 右侧隐藏
            edge_area = QRect(window_geometry.x(), 
                             window_geometry.y(), 5, window_geometry.height())
            if edge_area.contains(cursor_pos):
                print(f"检测到鼠标在右侧隐藏窗口边缘，位置: {cursor_pos}")
                self.mouse_hovering = True
                self.hover_timer.start(100)
            else:
                print(f"鼠标不在右侧隐藏窗口边缘: {cursor_pos}")
                self.mouse_hovering = False
                self.hover_timer.stop()
            return
        
        # 顶部隐藏：检测鼠标是否在窗口底部5像素区域内
        elif window_geometry.y() < 0:  # 顶部隐藏
            edge_area = QRect(window_geometry.x(), 
                             window_geometry.y() + window_geometry.height() - 5, 
                             window_geometry.width(), 5)
            if edge_area.contains(cursor_pos):
                print(f"检测到鼠标在顶部隐藏窗口边缘，位置: {cursor_pos}")
                self.mouse_hovering = True
                self.hover_timer.start(100)
            else:
                print(f"鼠标不在顶部隐藏窗口边缘: {cursor_pos}")
                self.mouse_hovering = False
                self.hover_timer.stop()
            return
    
    def check_global_mouse_hover(self):
        """全局鼠标位置检测 - 只在软件隐藏位置的边缘时触发"""
        if not self.is_hidden:
            return
        
        # 获取鼠标位置
        cursor_pos = QCursor.pos()
        
        # 获取窗口当前几何信息（隐藏后的位置）
        window_geometry = self.geometry()
        
        # 获取屏幕几何信息
        screen = QApplication.primaryScreen()
        screen_geometry = screen.geometry()
        
        # 检查鼠标是否在软件隐藏位置的边缘
        # 左侧隐藏：检测鼠标是否在窗口右侧5像素区域内
        if window_geometry.x() < 0:  # 左侧隐藏
            edge_area = QRect(window_geometry.x() + window_geometry.width() - 5, 
                             window_geometry.y(), 5, window_geometry.height())
            if edge_area.contains(cursor_pos):
                print(f"全局检测到鼠标在左侧隐藏窗口边缘，位置: {cursor_pos}")
                self.mouse_hovering = True
                self.hover_timer.start(100)
            else:
                self.mouse_hovering = False
                self.hover_timer.stop()
            return
        
        # 右侧隐藏：检测鼠标是否在窗口左侧5像素区域内
        elif window_geometry.x() + window_geometry.width() > screen_geometry.width():  # 右侧隐藏
            edge_area = QRect(window_geometry.x(), 
                             window_geometry.y(), 5, window_geometry.height())
            if edge_area.contains(cursor_pos):
                print(f"全局检测到鼠标在右侧隐藏窗口边缘，位置: {cursor_pos}")
                self.mouse_hovering = True
                self.hover_timer.start(100)
            else:
                self.mouse_hovering = False
                self.hover_timer.stop()
            return
        
        # 顶部隐藏：检测鼠标是否在窗口底部5像素区域内
        elif window_geometry.y() < 0:  # 顶部隐藏
            edge_area = QRect(window_geometry.x(), 
                             window_geometry.y() + window_geometry.height() - 5, 
                             window_geometry.width(), 5)
            if edge_area.contains(cursor_pos):
                print(f"全局检测到鼠标在顶部隐藏窗口边缘，位置: {cursor_pos}")
                self.mouse_hovering = True
                self.hover_timer.start(100)
            else:
                self.mouse_hovering = False
                self.hover_timer.stop()
            return
    
    def enterEvent(self, event):
        """鼠标进入窗口事件 - 只在软件隐藏位置的边缘时触发"""
        print(f"enterEvent 触发")
        
        # 获取鼠标位置
        cursor_pos = QCursor.pos()
        
        # 获取窗口当前几何信息（隐藏后的位置）
        window_geometry = self.geometry()
        
        # 获取屏幕几何信息
        screen = QApplication.primaryScreen()
        screen_geometry = screen.geometry()
        
        # 检查鼠标是否在软件隐藏位置的边缘
        if self.is_hidden:
            # 左侧隐藏：检测鼠标是否在窗口右侧5像素区域内
            if window_geometry.x() < 0:  # 左侧隐藏
                edge_area = QRect(window_geometry.x() + window_geometry.width() - 5, 
                                 window_geometry.y(), 5, window_geometry.height())
                if edge_area.contains(cursor_pos):
                    # 延迟检测，避免意外触发
                    self.hover_timer.start(100)
                    print(f"鼠标进入左侧隐藏窗口边缘，位置: {cursor_pos}，启动延迟检测")
                    return
            # 右侧隐藏：检测鼠标是否在窗口左侧5像素区域内
            elif window_geometry.x() + window_geometry.width() > screen_geometry.width():  # 右侧隐藏
                edge_area = QRect(window_geometry.x(), 
                                 window_geometry.y(), 5, window_geometry.height())
                if edge_area.contains(cursor_pos):
                    # 延迟检测，避免意外触发
                    self.hover_timer.start(100)
                    print(f"鼠标进入右侧隐藏窗口边缘，位置: {cursor_pos}，启动延迟检测")
                    return
            # 顶部隐藏：检测鼠标是否在窗口底部5像素区域内
            elif window_geometry.y() < 0:  # 顶部隐藏
                edge_area = QRect(window_geometry.x(), 
                                 window_geometry.y() + window_geometry.height() - 5, 
                                 window_geometry.width(), 5)
                if edge_area.contains(cursor_pos):
                    # 延迟检测，避免意外触发
                    self.hover_timer.start(100)
                    print(f"鼠标进入顶部隐藏窗口边缘，位置: {cursor_pos}，启动延迟检测")
                    return
            
            print(f"鼠标未在隐藏窗口边缘，位置: {cursor_pos}，不触发相关操作")
        else:
            # 如果窗口未隐藏，标记为悬停状态，防止自动隐藏
            self.mouse_hovering = True
            self.hover_start_time = QDateTime.currentDateTime()
            print(f"鼠标进入窗口区域，位置: {cursor_pos}，设置悬停状态，防止自动隐藏")
    
    def leaveEvent(self, event):
        """鼠标离开窗口事件"""
        print(f"leaveEvent 触发")
        
        if self.is_hidden:
            # 停止悬停检测
            self.hover_timer.stop()
            self.mouse_hovering = False
            print("鼠标离开隐藏窗口区域，停止检测")
        else:
            # 获取当前鼠标位置
            cursor_pos = QCursor.pos()
            
            # 获取窗口当前位置
            window_geometry = self.geometry()
            window_rect = QRect(window_geometry.x(), window_geometry.y(), 
                              window_geometry.width(), window_geometry.height())
            
            print(f"leaveEvent 检测 - 鼠标位置: {cursor_pos}, 窗口区域: {window_rect}")
            
            # 如果鼠标仍在窗口区域内，不触发自动隐藏检测
            if window_rect.contains(cursor_pos):
                print("鼠标仍在窗口区域内，不触发自动隐藏检测")
                return
            
            print("鼠标确实离开了窗口区域，准备触发自动隐藏检测")
            
            # 如果窗口未隐藏，清除悬停状态
            self.mouse_hovering = False
            self.hover_start_time = None
            
            # 如果用户正在交互，不触发自动隐藏检测
            if not self.user_interacting:
                print("用户未在交互，延迟3秒后检查自动隐藏")
                # 延迟一点时间再检查是否需要隐藏，避免立即隐藏
                QTimer.singleShot(3000, self.check_auto_hide)
            else:
                print("用户正在交互，不触发自动隐藏检测")
    
    def closeEvent(self, event):
        """窗口关闭时保存状态并彻底退出应用程序"""
        # 保存窗口状态
        self.saveWindowState()
        
        # 停止所有计时器
        self.saveStateTimer.stop()
        self.mouse_check_timer.stop()
        self.hover_timer.stop()
        self.auto_hide_timer.stop()
        
        # 检查是否需要关闭时版本比对和上传
        if self.check_version_on_close():
            # 如果用户选择了上传或取消，则不关闭程序
            event.ignore()
            return
        
        # 接受关闭事件
        event.accept()
        
        # 使用计时器延迟调用quit()，确保closeEvent完全处理完毕
        QTimer.singleShot(100, QApplication.instance().quit)

    def check_version_on_close(self):
        """关闭时检查版本号并提示上传"""
        try:
            # 检查是否有WebDAV配置
            settings = QSettings("TodoApp", "WebDAVSync")
            webdav_url = settings.value("url", "")
            webdav_username = settings.value("username", "")
            encrypted_password = settings.value("password", "")
            webdav_remote_path = settings.value("remote_path", "")
            
            # 解密密码
            if encrypted_password:
                from webdav_sync import decrypt_password
                webdav_password = decrypt_password(encrypted_password)
            else:
                webdav_password = ""
            
            # 如果没有配置WebDAV，直接关闭
            if not webdav_url or not webdav_username or not webdav_password:
                print("未配置WebDAV，直接关闭程序")
                return False
            
            # 获取本地版本号
            local_version = self.get_local_version()
            if local_version is None:
                print("无法获取本地版本号，直接关闭程序")
                return False
            
            # 获取远程版本号
            remote_version = self.get_remote_version(webdav_url, webdav_username, webdav_password, webdav_remote_path)
            if remote_version is None:
                # 远程文件不存在，提示是否上传
                return self.show_close_upload_dialog(local_version, None, "首次上传")
            
            # 比较版本号
            if local_version > remote_version:
                # 本地版本更新，提示上传
                return self.show_close_upload_dialog(local_version, remote_version, "上传")
            elif local_version < remote_version:
                # 远程版本更新，提示是否覆盖本地
                return self.show_close_restore_dialog(local_version, remote_version)
            else:
                # 版本号相同，直接关闭
                print(f"版本号一致: {local_version}，直接关闭程序")
                return False
                
        except Exception as e:
            print(f"关闭时版本比对失败: {e}")
            return False

    def show_close_upload_dialog(self, local_version, remote_version, action):
        """显示关闭时上传对话框"""
        from PyQt5.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QMessageBox
        
        dialog = QDialog(self)
        dialog.setWindowTitle("关闭前数据同步")
        dialog.setFixedSize(450, 250)
        dialog.setModal(True)
        
        layout = QVBoxLayout(dialog)
        
        # 版本信息
        if remote_version is None:
            info_text = f"""
检测到本地数据尚未同步到云端：

本地版本: {local_version}
远程版本: 无（首次上传）

建议在关闭前上传数据到云端，确保数据安全。
            """
        else:
            info_text = f"""
检测到本地数据有更新：

本地版本: {local_version}
远程版本: {remote_version}

建议在关闭前上传最新数据到云端。
            """
        
        info_label = QLabel(info_text)
        info_label.setWordWrap(True)
        layout.addWidget(info_label)
        
        # 按钮布局
        button_layout = QHBoxLayout()
        
        # 上传按钮
        upload_button = QPushButton("上传到云端")
        upload_button.clicked.connect(lambda: self.close_upload_to_remote(dialog))
        button_layout.addWidget(upload_button)
        
        # 跳过按钮
        skip_button = QPushButton("跳过并关闭")
        skip_button.clicked.connect(dialog.accept)
        button_layout.addWidget(skip_button)
        
        layout.addLayout(button_layout)
        
        # 显示对话框
        result = dialog.exec_()
        return False  # 无论选择哪个选项，都允许关闭程序

    def show_close_restore_dialog(self, local_version, remote_version):
        """显示关闭时恢复对话框"""
        from PyQt5.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QMessageBox
        
        dialog = QDialog(self)
        dialog.setWindowTitle("关闭前数据同步")
        dialog.setFixedSize(450, 250)
        dialog.setModal(True)
        
        layout = QVBoxLayout(dialog)
        
        # 版本信息
        info_text = f"""
检测到远程数据有更新：

本地版本: {local_version}
远程版本: {remote_version}

建议在关闭前恢复最新数据，或先上传本地数据。        """
        
        info_label = QLabel(info_text)
        info_label.setWordWrap(True)
        layout.addWidget(info_label)
        
        # 按钮布局
        button_layout = QHBoxLayout()
        
        # 恢复按钮
        restore_button = QPushButton("恢复远程数据")
        restore_button.clicked.connect(lambda: self.restore_from_remote(dialog))
        button_layout.addWidget(restore_button)
        
        # 上传按钮
        upload_button = QPushButton("上传本地数据")
        upload_button.clicked.connect(lambda: self.close_upload_to_remote(dialog))
        button_layout.addWidget(upload_button)
        
        # 跳过按钮
        skip_button = QPushButton("跳过并关闭")
        skip_button.clicked.connect(dialog.accept)
        button_layout.addWidget(skip_button)
        
        layout.addLayout(button_layout)
        
        # 显示对话框
        result = dialog.exec_()
        return False  # 无论选择哪个选项，都允许关闭程序

    def close_upload_to_remote(self, dialog):
        """关闭时上传到远程"""
        try:
            from webdav_sync import WebDAVWorker, decrypt_password
            
            # 显示上传进度提示
            self.show_upload_progress("正在上传数据到云端...")
            
            # 获取WebDAV配置
            settings = QSettings("TodoApp", "WebDAVSync")
            url = settings.value("url", "")
            username = settings.value("username", "")
            encrypted_password = settings.value("password", "")
            remote_path = settings.value("remote_path", "")
            
            # 解密密码
            if encrypted_password:
                password = decrypt_password(encrypted_password)
            else:
                password = ""
            
            # 创建上传线程
            self.close_upload_worker = WebDAVWorker(url, username, password, self.db_file, "upload", remote_path)
            self.close_upload_worker.finished_signal.connect(lambda success, msg: self.on_close_upload_finished(success, msg, dialog))
            self.close_upload_worker.start()
            
        except Exception as e:
            self.hide_upload_progress()  # 隐藏进度提示
            QMessageBox.warning(self, "错误", f"上传失败: {e}")
            dialog.accept()

    def close_restore_from_remote(self, dialog):
        """关闭时从远程恢复"""
        try:
            from webdav_sync import WebDAVWorker, decrypt_password
            
            # 显示恢复进度提示
            self.show_upload_progress("正在从云端恢复数据...")
            
            # 获取WebDAV配置
            settings = QSettings("TodoApp", "WebDAVSync")
            url = settings.value("url", "")
            username = settings.value("username", "")
            encrypted_password = settings.value("password", "")
            remote_path = settings.value("remote_path", "")
            
            # 解密密码
            if encrypted_password:
                password = decrypt_password(encrypted_password)
            else:
                password = ""
            
            # 创建下载线程
            self.close_restore_worker = WebDAVWorker(url, username, password, self.db_file, "download", remote_path)
            self.close_restore_worker.finished_signal.connect(lambda success, msg: self.on_close_restore_finished(success, msg, dialog))
            self.close_restore_worker.start()
            
        except Exception as e:
            self.hide_upload_progress()  # 隐藏进度提示
            QMessageBox.warning(self, "错误", f"恢复失败: {e}")
            dialog.accept()

    def on_close_upload_finished(self, success, message, dialog):
        """关闭时上传完成回调"""
        # 隐藏上传进度提示
        self.hide_upload_progress()
        
        if success:
            QMessageBox.information(self, "成功", "数据上传成功！程序将关闭。")
        else:
            QMessageBox.warning(self, "失败", f"数据上传失败: {message}")
        
        dialog.accept()

    def on_close_restore_finished(self, success, message, dialog):
        """关闭时恢复完成回调"""
        # 隐藏恢复进度提示
        self.hide_upload_progress()
        
        if success:
            QMessageBox.information(self, "成功", "数据恢复成功！程序将关闭。")
            # 重新加载数据
            self.loadData()
        else:
            QMessageBox.warning(self, "失败", f"数据恢复失败: {message}")
        
        dialog.accept()



    # 添加窗口大小改变事件处理，确保列表项更新
    def resizeEvent(self, event):
        """窗口大小改变事件处理"""
        super().resizeEvent(event)
        # 在窗口大小变化后，更新列表项大小
        self.updateListItemSize()
        
        # 重新创建圆角掩码，确保圆角区域透明
        self.createRoundedMask()
        
        # 延迟保存窗口状态，避免频繁保存
        if hasattr(self, 'resize_save_timer'):
            self.resize_save_timer.stop()
        else:
            from PyQt5.QtCore import QTimer
            self.resize_save_timer = QTimer()
            self.resize_save_timer.setSingleShot(True)
            self.resize_save_timer.timeout.connect(self.saveWindowState)
        
        # 500毫秒后保存窗口状态
        self.resize_save_timer.start(500)

    def updateListItemSize(self):
        """更新所有列表项的大小以适应视口宽度变化"""
        # 更新待办列表所有项目
        todo_viewport_width = self.todo_list_widget.viewport().width()
        safe_todo_width = max(200, todo_viewport_width - 15)  # 确保最小宽度
        
        for i in range(self.todo_list_widget.count()):
            item = self.todo_list_widget.item(i)
            widget = self.todo_list_widget.itemWidget(item)
            if widget:
                # 重新计算大小
                widget._truncateTextWithEllipsis()  # 重新截断文本
                size_hint = widget.sizeHint()
                item.setSizeHint(QSize(safe_todo_width, size_hint.height()))
        
        # 更新已完成列表所有项目
        completed_viewport_width = self.completed_list_widget.viewport().width()
        safe_completed_width = max(200, completed_viewport_width - 15)  # 确保最小宽度
        
        for i in range(self.completed_list_widget.count()):
            item = self.completed_list_widget.item(i)
            widget = self.completed_list_widget.itemWidget(item)
            if widget:
                # 重新计算大小
                widget._truncateTextWithEllipsis()  # 重新截断文本
                size_hint = widget.sizeHint()
                item.setSizeHint(QSize(safe_completed_width, size_hint.height()))

    # 添加自启动相关方法
    def toggleAutostart(self, checked):
        """启用或禁用开机自启动，同时控制自动登录"""
        try:
            if checked:
                self.enableAutostart()
                self.autostart_button.setChecked(True)
                # 同时启用自动登录
                self.settings.setValue("auto_login", True)
                print("已启用自动登录")
            else:
                self.disableAutostart()
                self.autostart_button.setChecked(False)
                # 同时禁用自动登录
                self.settings.setValue("auto_login", False)
                print("已禁用自动登录")
        except Exception as e:
            QMessageBox.warning(self, "自启动设置失败", f"无法设置自启动: {str(e)}")
            # 恢复按钮状态
            self.autostart_button.setChecked(self.isAutostartEnabled())
    

    

        




    # 添加WebDAV云同步设置对话框打开方法
    def open_cloud_settings(self):
        try:
            # 数据库名称已经包含机器码，直接打开WebDAV云设置对话框
            dialog = WebDAVSettingsDialog(self, self.db_file)
            # 连接高度设置改变信号
            dialog.height_setting_changed.connect(self.refreshListsForHeightChange)
            dialog.exec_()
            
        except Exception as e:
            QMessageBox.warning(self, "错误", f"打开WebDAV云设置时出错：{str(e)}")
    
    # ==================== 插件相关方法 ====================
    
    def load_plugin_config(self):
        """加载插件配置"""
        try:
            config_path = get_config_file_path()
            print(f"主窗口加载插件配置: {config_path}")
            if os.path.exists(config_path):
                with open(config_path, 'r', encoding='utf-8') as f:
                    config = json.load(f)
                    print(f"主窗口成功加载插件配置，当前提供商: {config.get('current_provider', config.get('api_provider'))}")
                    return config
            else:
                print(f"插件配置文件不存在: {config_path}")
        except Exception as e:
            print(f"加载插件配置失败: {e}")
            pass
        return {
            'api_key': '',
            'api_provider': 'DeepSeek',
            'base_url': 'https://api.deepseek.com',
            'model': 'deepseek-chat',
            'enable_todo_analysis': False,
            'auto_save': True,
            'output_format': 'text',
            'theme': 'light'
        }
    
    def save_plugin_config(self, config):
        """保存插件配置"""
        try:
            config_path = get_config_file_path()
            print(f"主窗口保存插件配置到: {config_path}")
            with open(config_path, 'w', encoding='utf-8') as f:
                json.dump(config, f, ensure_ascii=False, indent=2)
            print("主窗口插件配置保存成功")
        except Exception as e:
            print(f"主窗口保存插件配置失败: {e}")
            QMessageBox.critical(self, "错误", f"保存配置失败: {str(e)}")
    
    def show_centered_message(self, parent_window, icon, title, text, buttons=QMessageBox.Ok):
        """显示居中的消息框"""
        msg_box = QMessageBox(icon, title, text, buttons, self)
        
        # 获取父窗口几何信息
        parent_rect = parent_window.geometry()
        msg_box_size = msg_box.sizeHint()
        
        # 计算父窗口中心位置
        center_x = parent_rect.center().x() - msg_box_size.width() // 2
        center_y = parent_rect.center().y() - msg_box_size.height() // 2
        
        # 确保窗口不会超出屏幕边界
        screen = QApplication.primaryScreen()
        if screen:
            screen_rect = screen.availableGeometry()
            center_x = max(screen_rect.left(), min(center_x, screen_rect.right() - msg_box_size.width()))
            center_y = max(screen_rect.top(), min(center_y, screen_rect.bottom() - msg_box_size.height()))
        
        # 设置消息框位置
        msg_box.move(center_x, center_y)
        
        # 执行消息框
        return msg_box.exec_()
    
    def show_plugin_config(self):
        """显示插件配置界面"""
        config = self.load_plugin_config()
        dialog = PluginConfigDialog(self, config)
        
        # 确保配置窗口始终保持在AI插件界面中心区域
        if hasattr(self, 'ai_plugin_window') and self.ai_plugin_window:
            # 获取AI插件窗口几何信息
            plugin_rect = self.ai_plugin_window.geometry()
            dialog_size = dialog.size()
            
            # 计算AI插件窗口中心位置
            center_x = plugin_rect.center().x() - dialog_size.width() // 2
            center_y = plugin_rect.center().y() - dialog_size.height() // 2
            
            # 确保窗口不会超出屏幕边界
            screen = QApplication.primaryScreen()
            if screen:
                screen_rect = screen.availableGeometry()
                center_x = max(screen_rect.left(), min(center_x, screen_rect.right() - dialog_size.width()))
                center_y = max(screen_rect.top(), min(center_y, screen_rect.bottom() - dialog_size.height()))
            
            # 设置窗口位置
            dialog.move(center_x, center_y)
        
        if dialog.exec_() == QDialog.Accepted:
            self.save_plugin_config(dialog.config)
            # 立即应用新配置，无需重启
            self.apply_plugin_config_immediately(dialog.config)
            
            # 显示居中的成功消息框
            if hasattr(self, 'ai_plugin_window') and self.ai_plugin_window:
                self.show_centered_message(self.ai_plugin_window, QMessageBox.Information, "成功", "配置已保存并立即生效！")
            else:
                self.show_centered_message(self, QMessageBox.Information, "成功", "配置已保存并立即生效！")
    
    def apply_plugin_config_immediately(self, config):
        """立即应用插件配置，无需重启"""
        try:
            # 如果AI插件窗口已经打开，更新其配置
            if hasattr(self, 'ai_plugin_window') and self.ai_plugin_window:
                # 更新AI插件窗口的配置
                self.ai_plugin_window.config = config
                
                # 重新加载配置到AI插件窗口
                if hasattr(self.ai_plugin_window, 'load_config'):
                    self.ai_plugin_window.load_config()
                    
                # 应用主题变化
                if hasattr(self.ai_plugin_window, 'apply_theme'):
                    self.ai_plugin_window.apply_theme()
                    
                print("AI插件配置已立即更新")
                
        except Exception as e:
            print(f"应用插件配置时出错: {e}")
    
    def launchAIPlugin(self):
        """启动AI插件界面（内嵌版本）"""
        try:
            # 创建并显示AI插件窗口
            self.ai_plugin_window = AIPluginWindow(self)
            
            # 计算窗口居中位置
            parent_rect = self.geometry()
            window_size = self.ai_plugin_window.size()
            
            # 计算主窗口中心位置
            center_x = parent_rect.center().x() - window_size.width() // 2
            center_y = parent_rect.center().y() - window_size.height() // 2
            
            # 确保窗口不会超出屏幕边界
            screen = QApplication.primaryScreen()
            if screen:
                screen_rect = screen.availableGeometry()
                center_x = max(screen_rect.left(), min(center_x, screen_rect.right() - window_size.width()))
                center_y = max(screen_rect.top(), min(center_y, screen_rect.bottom() - window_size.height()))
            
            # 设置窗口位置并显示
            self.ai_plugin_window.move(center_x, center_y)
            self.ai_plugin_window.show()
            self.ai_plugin_window.raise_()
            self.ai_plugin_window.activateWindow()
            
            print("AI插件窗口已启动并居中显示")
            
        except Exception as e:
            QMessageBox.critical(self, "错误", f"启动AI插件时发生错误：\n{str(e)}")
            print(f"启动AI插件失败: {str(e)}")
            import traceback
            traceback.print_exc()
    
    def check_version_on_startup(self):
        """启动时检查版本号并提示备份或恢复"""
        try:
            # 检查是否有WebDAV配置
            settings = QSettings("TodoApp", "WebDAVSync")
            webdav_url = settings.value("url", "")
            webdav_username = settings.value("username", "")
            encrypted_password = settings.value("password", "")
            webdav_remote_path = settings.value("remote_path", "")
            
            # 解密密码
            if encrypted_password:
                from webdav_sync import decrypt_password
                webdav_password = decrypt_password(encrypted_password)
            else:
                webdav_password = ""
            
            # 如果没有配置WebDAV，跳过检查
            if not webdav_url or not webdav_username or not webdav_password:
                print("未配置WebDAV，跳过版本号比对")
                return
            
            # 获取本地版本号
            local_version = self.get_local_version()
            if local_version is None:
                print("无法获取本地版本号")
                return
            
            # 获取远程版本号
            remote_version = self.get_remote_version(webdav_url, webdav_username, webdav_password, webdav_remote_path)
            if remote_version is None:
                print("无法获取远程版本号，可能是首次使用或网络问题")
                return
            
            # 比较版本号
            if local_version < remote_version:
                # 远程版本更新，提示恢复
                self.show_version_dialog("恢复", local_version, remote_version, "remote")
            elif local_version > remote_version:
                # 本地版本更新，提示备份
                self.show_version_dialog("备份", local_version, remote_version, "local")
            else:
                # 版本号相同，无需操作
                print(f"版本号一致: {local_version}")
                
        except Exception as e:
            print(f"版本号比对失败: {e}")
    
    def get_local_version(self):
        """获取本地数据库版本号"""
        try:
            conn = sqlite3.connect(self.db_file)
            cursor = conn.cursor()
            cursor.execute("SELECT version FROM database_version WHERE id = 1")
            result = cursor.fetchone()
            conn.close()
            return result[0] if result else None
        except Exception as e:
            print(f"获取本地版本号失败: {e}")
            return None



    def reset_original_geometry(self):
        """重置原始几何信息"""
        self.original_geometry = None
        print("已重置原始几何信息")
    
    def init_plugin_refresh_monitor(self):
        """初始化插件刷新监控"""
        # 创建定时器来监控插件刷新标记文件
        self.plugin_refresh_timer = QTimer(self)
        self.plugin_refresh_timer.timeout.connect(self.check_plugin_refresh)
        self.plugin_refresh_timer.start(1000)  # 每秒检查一次
        print("插件刷新监控已启动")
        
        # 记录上次检查的文件修改时间
        self.last_refresh_check_time = 0
        
    def check_plugin_refresh(self):
        """检查插件刷新标记文件"""
        try:
            refresh_marker_file = 'refresh_todo_list.txt'
            if os.path.exists(refresh_marker_file):
                # 获取文件修改时间
                file_mtime = os.path.getmtime(refresh_marker_file)
                
                # 如果文件是新创建的或修改的
                if file_mtime > self.last_refresh_check_time:
                    print(f"检测到插件刷新标记文件更新: {refresh_marker_file}")
                    
                    # 读取文件内容
                    try:
                        with open(refresh_marker_file, 'r', encoding='utf-8') as f:
                            content = f.read()
                            print(f"刷新标记文件内容: {content}")
                    except Exception as e:
                        print(f"读取刷新标记文件失败: {str(e)}")
                    
                    # 刷新待办列表
                    self.loadData()
                    print("已刷新待办列表")
                    
                    # 删除标记文件
                    try:
                        os.remove(refresh_marker_file)
                        print("已删除刷新标记文件")
                    except Exception as e:
                        print(f"删除刷新标记文件失败: {str(e)}")
                    
                    # 更新检查时间
                    self.last_refresh_check_time = file_mtime
                    
        except Exception as e:
            print(f"检查插件刷新标记文件时出错: {str(e)}")

    def check_software_update_silent(self):
        """静默检查软件更新，不显示进度提示"""
        try:
            print("正在静默检查软件更新...")
            
            # 创建更新检查线程
            self.silent_update_check_thread = UpdateCheckThread()
            self.silent_update_check_thread.update_available.connect(self.on_silent_update_available)
            self.silent_update_check_thread.no_update.connect(self.on_silent_no_update)
            self.silent_update_check_thread.error_occurred.connect(self.on_silent_update_error)
            self.silent_update_check_thread.start()
        except Exception as e:
            print(f"静默检查更新时发生错误：{str(e)}")
    
    def on_silent_update_available(self, version, changelog, download_url):
        """静默检查发现新版本"""
        print(f"静默检查发现新版本: {version}")
        # 显示更新提示
        self.update_hint_label.setVisible(True)
        # 保存更新信息
        self.pending_update_info = {
            'version': version,
            'changelog': changelog,
            'download_url': download_url
        }
    
    def on_silent_no_update(self):
        """静默检查已是最新版本"""
        print("静默检查：当前已是最新版本")
        # 隐藏更新提示
        self.update_hint_label.setVisible(False)
        self.pending_update_info = None
    
    def on_silent_update_error(self, error_msg):
        """静默检查出错"""
        print(f"静默检查更新出错：{error_msg}")
        # 错误时不显示更新提示
        self.update_hint_label.setVisible(False)

    def check_for_updates(self):
        """手动检查软件更新"""
        try:
            # 如果已经有待更新信息，直接显示更新对话框
            if hasattr(self, 'pending_update_info') and self.pending_update_info:
                info = self.pending_update_info
                self.show_update_dialog(info['version'], info['changelog'], info['download_url'])
                return
            
            print("正在检查更新...")
            # 显示进度提示
            self.show_upload_progress("正在检查更新...")

            # 创建更新检查线程
            self.update_check_thread = UpdateCheckThread()
            self.update_check_thread.update_available.connect(self.on_update_available)
            self.update_check_thread.no_update.connect(self.on_no_update)
            self.update_check_thread.error_occurred.connect(self.on_update_error)
            self.update_check_thread.start()

        except Exception as e:
            self.hide_upload_progress()
            QMessageBox.warning(self, "检查失败", f"启动更新检查时发生错误：\n{str(e)}")
    
    def on_update_available(self, version, changelog, download_url):
        """有新版本可用"""
        self.hide_upload_progress()
        self.show_update_dialog(version, changelog, download_url)
    
    def on_no_update(self):
        """已是最新版本"""
        self.hide_upload_progress()
        # 使用进度提示代替对话框
        self.show_upload_progress("当前已是最新版本！", 2000)  # 显示2秒
    
    def on_update_error(self, error_msg):
        """更新检查出错"""
        self.hide_upload_progress()
        QMessageBox.warning(self, "检查失败", error_msg)

    def _compare_versions(self, version1, version2):
        """比较版本号，返回1表示version1大于version2，-1表示小于，0表示相等"""
        try:
            v1_parts = [int(x) for x in version1.replace('V', '').split('.')]
            v2_parts = [int(x) for x in version2.replace('V', '').split('.')]

            # 填充较短的版本号
            while len(v1_parts) < len(v2_parts):
                v1_parts.append(0)
            while len(v2_parts) < len(v1_parts):
                v2_parts.append(0)

            for i in range(len(v1_parts)):
                if v1_parts[i] > v2_parts[i]:
                    return 1
                elif v1_parts[i] < v2_parts[i]:
                    return -1
            return 0
        except:
            return 0

    def show_update_dialog(self, version, changelog, download_url):
        """显示更新提示对话框"""
        msg_box = QMessageBox(self)
        msg_box.setWindowTitle("发现新版本")
        msg_box.setIcon(QMessageBox.Information)

        # 设置消息内容
        message = f"发现新版本 {version}！\n\n更新内容：\n{changelog}\n\n是否立即下载更新？"
        msg_box.setText(message)

        # 设置对话框固定宽度为400像素
        msg_box.setFixedWidth(400)
        
        # 设置样式，让对话框更美观
        msg_box.setStyleSheet("""
            QMessageBox {
                background-color: #f8f9fa;
                border-radius: 8px;
            }
            QMessageBox QLabel {
                color: #2c3e50;
                font-size: 12px;
                font-family: 'Microsoft YaHei';
                line-height: 1.5;
                padding: 10px;
            }
            QMessageBox QPushButton {
                min-width: 80px;
                min-height: 30px;
                padding: 5px 15px;
                background-color: #4895ef;
                color: white;
                border: none;
                border-radius: 6px;
                font-weight: bold;
                font-family: 'Microsoft YaHei';
                font-size: 12px;
            }
            QMessageBox QPushButton:hover {
                background-color: #3b82f6;
            }
            QMessageBox QPushButton:pressed {
                background-color: #2563eb;
            }
        """)

        # 添加按钮
        download_button = msg_box.addButton("立即下载", QMessageBox.AcceptRole)
        later_button = msg_box.addButton("稍后提醒", QMessageBox.RejectRole)
        cancel_button = msg_box.addButton("取消", QMessageBox.RejectRole)

        msg_box.setDefaultButton(download_button)
        msg_box.setEscapeButton(cancel_button)

        # 执行对话框
        result = msg_box.exec_()

        if msg_box.clickedButton() == download_button:
            # 开始下载
            self.download_update(version, download_url)
        elif msg_box.clickedButton() == later_button:
            # 稍后提醒（这里可以保存提醒时间）
            QMessageBox.information(self, "稍后提醒", "将在下次启动时再次提醒更新。")

    def download_update(self, version, download_url):
        """下载新版本"""
        try:
            print(f"开始下载新版本: {download_url}")

            # 显示下载进度
            self.show_upload_progress("正在下载更新...")

            # 创建下载线程
            self.download_thread = UpdateDownloadThread(download_url)
            self.download_thread.progress_updated.connect(self.on_download_progress_updated)
            self.download_thread.download_finished.connect(lambda success, file_path: self.on_download_finished(version, success, file_path))
            self.download_thread.download_error.connect(self.on_download_error)
            self.download_thread.start()

        except Exception as e:
            self.hide_upload_progress()
            QMessageBox.critical(self, "下载失败", f"启动下载时发生错误：\n{str(e)}")
            print(f"启动下载失败: {e}")
    
    def on_download_progress_updated(self, message):
        """下载进度更新"""
        import time
        current_time = time.time()
        
        # 节流机制：限制更新频率
        if current_time - self.last_progress_update_time >= self.progress_update_interval:
            self.last_progress_update_time = current_time
            # 使用QTimer.singleShot来确保进度更新不会阻塞UI
            QTimer.singleShot(0, lambda: self.show_upload_progress(message))
    
    def on_download_finished(self, version, success, file_path):
        """下载完成"""
        self.hide_upload_progress()
        
        if success == "success":
            # 显示下载完成提示
            msg_box = QMessageBox(self)
            msg_box.setWindowTitle("下载完成")
            msg_box.setIcon(QMessageBox.Information)
            msg_box.setText(f"新版本 {version} 已下载完成！\n\n文件位置: {file_path}\n\n点击「立即安装」将启动新版本并关闭当前程序。\n是否立即安装更新？")

            install_button = msg_box.addButton("立即安装", QMessageBox.AcceptRole)
            later_button = msg_box.addButton("稍后安装", QMessageBox.RejectRole)

            msg_box.setDefaultButton(install_button)
            msg_box.exec_()

            if msg_box.clickedButton() == install_button:
                # 安装新版本到当前程序目录
                self.install_update(file_path, version)
            else:
                QMessageBox.information(self, "稍后安装", f"新版本已保存到:\n{file_path}\n\n请手动运行安装。")
        else:
            QMessageBox.critical(self, "下载失败", f"下载更新失败：\n{success}")
    
    def on_download_error(self, error_msg):
        """下载出错"""
        self.hide_upload_progress()
        QMessageBox.critical(self, "下载失败", error_msg)
    
    def install_update(self, new_exe_path, version):
        """完全独立于Python的更新安装方法"""
        try:
            print(f"开始安装更新版本 {version}...")
            self.show_upload_progress("正在安装更新...")
            
            # 获取当前程序路径信息
            current_exe_path = sys.executable
            current_dir = os.path.dirname(current_exe_path)
            current_exe_name = os.path.basename(current_exe_path)
            target_path = os.path.join(current_dir, current_exe_name)
            
            # 创建纯Windows批处理文件执行更新
            batch_content = f'''@echo off
:: 更新安装脚本 - 完全不依赖Python
echo 正在安装版本 {version}...

:: 创建备份
if exist "{current_exe_path}" (
    echo 创建程序备份...
    copy "{current_exe_path}" "{current_exe_path}.backup" >nul 2>&1
)

:: 替换文件
echo 正在替换程序文件...
taskkill /f /im "{current_exe_name}" >nul 2>&1
timeout /t 1 /nobreak >nul
copy "{new_exe_path}" "{target_path}" >nul 2>&1

:: 检查是否替换成功
if not exist "{target_path}" (
    echo 更新失败，恢复备份...
    if exist "{current_exe_path}.backup" (
        copy "{current_exe_path}.backup" "{target_path}" >nul 2>&1
    )
    echo 错误：更新失败，已恢复原版本
    pause
    exit /b 1
)

:: 创建终极可靠启动脚本
echo 创建终极启动方案...
set "restart_cmd=%TEMP%\\restart_{current_exe_name}.bat"
echo @echo off > "%restart_cmd%"
echo :: 记录日志 >> "%restart_cmd%"
echo echo [%date% %time%] 开始执行重启脚本 >> "%restart_cmd%"
echo echo 目标路径: {target_path} >> "%restart_cmd%"

echo :: 方法1 - 高优先级任务计划 >> "%restart_cmd%"
echo schtasks /create /tn "TodoAppRestarter" /tr "\"{target_path}\"" /sc once /st 00:00:05 /ru SYSTEM /rl HIGHEST /f >> "%restart_cmd%"
echo schtasks /run /tn "TodoAppRestarter" >> "%restart_cmd%"

echo :: 方法2 - RunOnce注册表项 >> "%restart_cmd%"
echo reg add "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce" /v "RestartTodoApp" /t REG_SZ /d "\"{target_path}\"" /f >> "%restart_cmd%"

echo :: 方法3 - 直接启动并重试 >> "%restart_cmd%"
echo set retry=0 >> "%restart_cmd%"
echo :retry_start >> "%restart_cmd%"
echo start "" "{target_path}" >> "%restart_cmd%"
echo timeout /t 1 /nobreak >> "%restart_cmd%"
echo tasklist | find "{current_exe_name}" >> "%restart_cmd%"
echo if %errorlevel% equ 0 goto success >> "%restart_cmd%"
echo set /a retry+=1 >> "%restart_cmd%"
echo if %retry% lss 3 goto retry_start >> "%restart_cmd%"

echo :: 所有方法都失败 >> "%restart_cmd%"
echo echo 错误：所有启动方法都失败！ >> "%restart_cmd%"
echo pause >> "%restart_cmd%"
echo exit /b 1 >> "%restart_cmd%"

echo :success >> "%restart_cmd%"
echo del "%%~f0" >> "%restart_cmd%"
echo exit /b 0 >> "%restart_cmd%"

:: 立即执行重启脚本并请求管理员权限（如果需要）
echo 执行终极重启方案...
echo set "batch_path=%~f0" > "%TEMP%\\runas_restart.bat"
echo %~dp0\\{current_exe_name} -update_restart >> "%TEMP%\\runas_restart.bat"
call "%restart_cmd%"

:: 立即退出当前程序
echo 更新成功，即将重启...
taskkill /f /im "{current_exe_name}" >nul 2>&1

:: 删除临时文件
del "%~f0" >nul 2>&1
'''
            # 创建临时批处理文件
            import tempfile
            with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f:
                f.write(batch_content)
                batch_path = f.name
            
            # 静默启动批处理文件（隐藏所有窗口）
            startupinfo = subprocess.STARTUPINFO()
            startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
            startupinfo.wShowWindow = 0
            subprocess.Popen([batch_path], shell=True,
                           startupinfo=startupinfo,
                           creationflags=subprocess.CREATE_NO_WINDOW)
            
            print("已启动独立更新流程")
            self.closeAndQuit()
            
        except Exception as e:
            print(f"更新安装失败: {e}")
            QMessageBox.critical(self, "更新错误", 
                f"自动更新失败:\n{str(e)}\n\n请手动替换文件:\n{new_exe_path}")
            self.hide_upload_progress()
            
        except Exception as e:
            self.hide_upload_progress()
            print(f"安装更新失败: {e}")
            import traceback
            traceback.print_exc()
            
            # 尝试恢复备份
            if os.path.exists(backup_path):
                try:
                    shutil.copy2(backup_path, current_exe_path)
                    print("已恢复备份")
                except:
                    pass
            
            QMessageBox.critical(self, "安装失败", f"安装更新失败：\n{str(e)}\n\n请手动安装或联系技术支持。")
    
    def force_replace_file(self, source_path, target_path):
        """强制替换文件（Windows）"""
        try:
            import ctypes
            from ctypes import wintypes
            
            # 使用Windows API强制替换文件
            MOVEFILE_REPLACE_EXISTING = 0x1
            MOVEFILE_WRITE_THROUGH = 0x8
            
            # 先尝试移动文件
            result = ctypes.windll.kernel32.MoveFileExW(
                source_path, 
                target_path, 
                MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH
            )
            
            if result:
                print("使用MoveFileEx成功替换")
                return True
            else:
                # 如果MoveFileEx失败，尝试使用批处理文件
                print("MoveFileEx失败，使用批处理文件替换")
                return self.replace_with_batch(source_path, target_path)
                
        except Exception as e:
            print(f"强制替换失败: {e}")
            return self.replace_with_batch(source_path, target_path)
    
    def replace_with_batch(self, source_path, target_path):
        """使用批处理文件替换"""
        try:
            import tempfile
            
            # 获取文件名用于进程终止
            current_exe_name = os.path.basename(target_path)
            
            # 创建更强大的批处理文件
            batch_content = f'''@echo off
echo 正在替换程序文件...
timeout /t 3 /nobreak >nul

REM 尝试多次替换
for /L %%i in (1,1,5) do (
    echo 尝试替换 %%i/5...
    
    REM 强制终止程序进程
    taskkill /f /im "{current_exe_name}" >nul 2>&1
    timeout /t 2 /nobreak >nul
    
    REM 删除目标文件
    if exist "{target_path}" (
        del /f /q "{target_path}" >nul 2>&1
    )
    
    REM 移动新文件
    move "{source_path}" "{target_path}" >nul 2>&1
    
    REM 检查是否成功
    if exist "{target_path}" (
        echo 替换成功！
        goto :success
    ) else (
        echo 替换失败，重试...
        timeout /t 1 /nobreak >nul
    )
)

echo 所有替换尝试都失败！

:success
del "%~f0"
'''
            
            with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f:
                f.write(batch_content)
                batch_path = f.name
            
            # 启动批处理文件
            startupinfo = subprocess.STARTUPINFO()
            startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
            startupinfo.wShowWindow = 0
            subprocess.Popen([batch_path], shell=True, 
                           startupinfo=startupinfo,
                           creationflags=subprocess.CREATE_NO_WINDOW)
            
            print("已启动批处理文件进行替换")
            return True
            
        except Exception as e:
            print(f"批处理替换失败: {e}")
            return False
    
    def restart_application(self):
        """更可靠的独立应用重启方法"""
        try:
            print("正在执行可靠重启流程...")
            
            # 获取程序路径信息
            current_exe_path = sys.executable
            current_dir = os.path.dirname(current_exe_path)
            current_exe_name = os.path.basename(current_exe_path)
            target_path = os.path.join(current_dir, current_exe_name)
            
            # 创建批处理文件确保可靠重启
            batch_content = f'''@echo off
:: 等待1秒确保当前程序完全退出
timeout /t 1 /nobreak >nul

:: 强制终止可能残留的进程
taskkill /f /im "{current_exe_name}" >nul 2>&1

:: 再次等待1秒
timeout /t 1 /nobreak >nul

:: 启动新版本
start "" "{target_path}"

:: 检查是否启动成功
if %ERRORLEVEL% neq 0 (
    echo 启动失败，错误代码: %ERRORLEVEL%
    pause
    exit /b 1
)

:: 删除临时文件
del "%~f0"
'''
            # 创建临时批处理文件
            import tempfile
            with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f:
                f.write(batch_content)
                batch_path = f.name
            
            # 启动批处理文件
            startupinfo = subprocess.STARTUPINFO()
            startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
            startupinfo.wShowWindow = 0
            subprocess.Popen([batch_path], shell=True,
                           startupinfo=startupinfo,
                           creationflags=subprocess.CREATE_NO_WINDOW)
            
            print("已启动可靠重启脚本")
            self.closeAndQuit()
            
        except Exception as e:
            print(f"重启失败: {e}")
            QMessageBox.critical(self, "重启错误", 
                f"自动重启失败:\n{str(e)}\n\n请手动启动程序:\n{target_path}")
            self.closeAndQuit()
            
        except Exception as e:
            print(f"重启失败: {e}")
            QMessageBox.warning(self, "重启失败", f"无法自动重启，请手动运行程序。\n\n程序路径: {target_path}")
            self.closeAndQuit()
    
    def force_restart(self):
        """强制重启应用程序 - 备用重启机制"""
        try:
            print("执行强制重启...")
            
            # 检查是否已经重启过
            if hasattr(self, 'restart_timer') and self.restart_timer.isActive():
                print("正常重启定时器仍在运行，取消强制重启")
                return
            
            # 获取当前程序路径
            current_exe_path = sys.executable
            current_dir = os.path.dirname(current_exe_path)
            current_exe_name = os.path.basename(current_exe_path)
            target_path = os.path.join(current_dir, current_exe_name)
            
            # 创建批处理文件来确保重启
            batch_content = f'''@echo off
timeout /t 1 /nobreak >nul
start "" "{target_path}"
taskkill /f /im "{current_exe_name}" >nul 2>&1
del "%~f0"
'''
            
            with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f:
                f.write(batch_content)
                batch_path = f.name
            
            # 启动批处理文件
            subprocess.Popen([batch_path], shell=True, 
                           creationflags=subprocess.CREATE_NO_WINDOW)
            
            print("强制重启批处理文件已启动")
            self.closeAndQuit()
            
        except Exception as e:
            print(f"强制重启失败: {e}")
            # 最后的尝试 - 直接退出
            self.closeAndQuit()
    
    def show_custom_tooltip(self, event, button, text):
        """显示自定义悬停提示在鼠标上方"""
        try:
            # 隐藏已存在的tooltip
            self.hide_custom_tooltip()
            
            # 创建自定义tooltip窗口
            self.custom_tooltip = QWidget()
            self.custom_tooltip.setWindowFlags(Qt.FramelessWindowHint | Qt.ToolTip)
            self.custom_tooltip.setAttribute(Qt.WA_TranslucentBackground)
            self.custom_tooltip.setAttribute(Qt.WA_DeleteOnClose, True)
            
            # 创建标签
            label = QLabel(text, self.custom_tooltip)
            label.setAlignment(Qt.AlignCenter)
            label.setStyleSheet("""
                QLabel {
                    background-color: rgba(51, 51, 51, 220);
                    color: white;
                    border: 1px solid rgba(85, 85, 85, 180);
                    border-radius: 4px;
                    padding: 6px 10px;
                    font-size: 12px;
                    font-family: 'Microsoft YaHei';
                }
            """)
            
            # 设置布局
            layout = QVBoxLayout(self.custom_tooltip)
            layout.addWidget(label)
            layout.setContentsMargins(0, 0, 0, 0)
            
            # 计算tooltip位置（鼠标上方）
            self.custom_tooltip.adjustSize()
            tooltip_size = self.custom_tooltip.size()
            
            # 获取鼠标全局位置
            cursor_pos = QCursor.pos()
            
            # 计算tooltip位置（鼠标上方15像素）
            tooltip_x = cursor_pos.x() - tooltip_size.width() // 2
            tooltip_y = cursor_pos.y() - tooltip_size.height() - 15
            
            # 确保不超出屏幕边界
            screen = QApplication.primaryScreen()
            screen_geometry = screen.geometry()
            
            if tooltip_x < screen_geometry.left():
                tooltip_x = screen_geometry.left()
            elif tooltip_x + tooltip_size.width() > screen_geometry.right():
                tooltip_x = screen_geometry.right() - tooltip_size.width()
                
            if tooltip_y < screen_geometry.top():
                tooltip_y = cursor_pos.y() + 15  # 如果上方放不下，就放在下方
            
            # 显示tooltip
            self.custom_tooltip.move(tooltip_x, tooltip_y)
            self.custom_tooltip.show()
            
        except Exception as e:
            print(f"显示自定义tooltip时出错: {e}")
    
    def hide_custom_tooltip(self):
        """隐藏自定义悬停提示"""
        try:
            if self.custom_tooltip:
                self.custom_tooltip.hide()
                self.custom_tooltip.deleteLater()
                self.custom_tooltip = None
        except Exception as e:
            print(f"隐藏自定义tooltip时出错: {e}")


def is_admin():
    """检查程序是否以管理员权限运行"""
    try:
        import ctypes
        return ctypes.windll.shell32.IsUserAnAdmin() != 0
    except:
        return False

def run_as_admin():
    """请求管理员权限重新运行程序"""
    import ctypes, sys
    
    # 如果不是管理员权限，则尝试提升权限
    if not is_admin():
        print("正在请求管理员权限...")
        try:
            result = ctypes.windll.shell32.ShellExecuteW(
                None, 
                "runas", 
                sys.executable, 
                " ".join(sys.argv), 
                None, 
                1  # SW_SHOWNORMAL
            )
            if result > 32:  # 成功启动新进程
                print("管理员权限进程已启动，当前进程退出")
                sys.exit(0)
            else:
                print(f"启动管理员权限失败，错误代码: {result}")
                print("继续以普通权限运行...")
        except Exception as e:
            print(f"请求管理员权限时出错: {e}")
            print("继续以普通权限运行...")
    else:
        print("当前已具有管理员权限")




class PasswordInputDialog(QDialog):
    """自定义密码输入对话框"""
    def __init__(self, parent=None, title="", message="", is_new_password=False):
        super().__init__(parent)
        self.setWindowTitle(title)
        self.setFixedSize(350, 200)
        
        # 设置图标
        if APP_ICON and os.path.exists(APP_ICON):
            self.setWindowIcon(QIcon(APP_ICON))
        
        # 设置窗口属性
        self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint | Qt.WindowStaysOnTopHint)
        
        # 布局
        layout = QVBoxLayout(self)
        layout.setSpacing(15)
        layout.setContentsMargins(20, 20, 20, 20)
        
        # 消息标签
        message_label = QLabel(message)
        message_label.setFont(QFont("Microsoft YaHei", 10))
        message_label.setStyleSheet(f"color: {COLORS['text_primary']};")
        layout.addWidget(message_label)
        
        # 密码输入框
        self.password_edit = QLineEdit()
        self.password_edit.setEchoMode(QLineEdit.Password)
        self.password_edit.setMinimumHeight(35)
        self.password_edit.setFont(QFont("Microsoft YaHei", 10))
        layout.addWidget(self.password_edit)
        
        # 如果是新密码，添加确认密码输入框
        if is_new_password:
            self.confirm_password_edit = QLineEdit()
            self.confirm_password_edit.setEchoMode(QLineEdit.Password)
            self.confirm_password_edit.setMinimumHeight(35)
            self.confirm_password_edit.setFont(QFont("Microsoft YaHei", 10))
            self.confirm_password_edit.setPlaceholderText("请再次输入密码确认")
            layout.addWidget(self.confirm_password_edit)
        else:
            self.confirm_password_edit = None
        
        # 按钮
        button_layout = QHBoxLayout()
        
        buttons = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel,
            Qt.Horizontal, self)
        buttons.button(QDialogButtonBox.Ok).setText("确定")
        buttons.button(QDialogButtonBox.Cancel).setText("取消")
        
        # 设置按钮宽度
        ok_button = buttons.button(QDialogButtonBox.Ok)
        cancel_button = buttons.button(QDialogButtonBox.Cancel)
        ok_button.setMinimumWidth(80)
        cancel_button.setMinimumWidth(80)
        
        buttons.accepted.connect(self.accept)
        buttons.rejected.connect(self.reject)
        
        button_layout.addStretch(1)
        button_layout.addWidget(buttons)
        layout.addLayout(button_layout)
        
        # 样式设置
        self.setStyleSheet(f"""
            QDialog {{
                background-color: {COLORS['background']};
                border-radius: 10px;
            }}
            QLineEdit {{
                border: 1px solid {COLORS['border']};
                border-radius: 6px;
                padding: 8px 12px;
                font-size: 12px;
                font-family: 'Microsoft YaHei';
                background-color: {COLORS['card_bg']};
            }}
            QLineEdit:focus {{
                border: 2px solid {COLORS['primary']};
            }}
            QPushButton {{
                min-height: 30px;
                min-width: 80px;
                padding: 0 15px;
                background-color: {COLORS['primary']};
                color: white;
                border: none;
                border-radius: 6px;
                font-weight: bold;
                font-family: 'Microsoft YaHei';
                font-size: 12px;
            }}
            QPushButton:hover {{
                background-color: {COLORS['accent']};
            }}
            QPushButton:pressed {{
                background-color: {COLORS['secondary']};
            }}
            QLabel {{
                font-family: 'Microsoft YaHei';
                font-size: 10pt;
            }}
        """)
        
        # 设置焦点到密码输入框
        self.password_edit.setFocus()
        
        # 连接回车键
        self.password_edit.returnPressed.connect(self.accept)
        if self.confirm_password_edit:
            self.confirm_password_edit.returnPressed.connect(self.accept)
    
    def getPassword(self):
        """获取输入的密码"""
        return self.password_edit.text()
    
    def getConfirmPassword(self):
        """获取确认密码"""
        if self.confirm_password_edit:
            return self.confirm_password_edit.text()
        return None
    
    def accept(self):
        """确认按钮处理"""
        password = self.getPassword()
        if not password:
            QMessageBox.warning(self, "提示", "请输入密码")
            return
        
        if self.confirm_password_edit:
            confirm_password = self.getConfirmPassword()
            if not confirm_password:
                QMessageBox.warning(self, "提示", "请再次输入密码确认")
                return
            
            if password != confirm_password:
                QMessageBox.warning(self, "提示", "两次输入的密码不一致")
                return
        
        super().accept()

# 在程序入口处调用
if __name__ == '__main__':
    try:
        print("=== 待办事项管理软件启动 ===")
        print(f"Python版本: {sys.version}")
        print(f"工作目录: {os.getcwd()}")
        
        # 设置异常处理钩子
        def exception_hook(exctype, value, traceback):
            print(f"未捕获的异常: {exctype.__name__}: {value}")
            import traceback as tb
            tb.print_exception(exctype, value, traceback)
        
        sys.excepthook = exception_hook
        
        print("跳过管理员权限检查，以普通权限运行")
        
        app = QApplication(sys.argv)
        print("QApplication创建成功")
        
        # 设置全局应用图标
        icon_path = APP_ICON
        if icon_path and os.path.exists(icon_path):
            app.setWindowIcon(QIcon(icon_path))
            print(f"已成功加载应用图标: {icon_path}")
        else:
            print("未找到图标文件，使用默认图标")
        
        # 启用窗口阴影和半透明特效
        app.setAttribute(Qt.AA_UseHighDpiPixmaps)
        app.setStyle("Fusion")
        print("应用样式设置完成")
        
        # 直接启动主程序，使用默认用户名
        print("正在创建主窗口...")
        main_window = MainWindow("default")
        print("主窗口创建成功")
        
        main_window.show()
        print("主窗口显示成功，进入事件循环")
        
        # 设置应用程序级别的异常处理（如果支持）
        try:
            def handle_app_exception(exctype, value, traceback):
                print(f"应用程序异常: {exctype.__name__}: {value}")
                import traceback as tb
                tb.print_exception(exctype, value, traceback)
                # 显示错误对话框
                try:
                    QMessageBox.critical(None, "程序错误", f"程序发生错误:\n{exctype.__name__}: {value}")
                except:
                    pass
            
            if hasattr(app, 'setExceptionHandler'):
                app.setExceptionHandler(handle_app_exception)
                print("已设置应用程序异常处理器")
            else:
                print("当前PyQt5版本不支持setExceptionHandler，使用系统异常钩子")
        except Exception as e:
            print(f"设置异常处理器失败: {e}")
        
        sys.exit(app.exec_())
        
    except Exception as e:
        print(f"程序启动异常: {e}")
        import traceback
        traceback.print_exc()
        
        # 尝试显示错误对话框
        try:
            from PyQt5.QtWidgets import QApplication, QMessageBox
            app = QApplication(sys.argv)
            QMessageBox.critical(None, "启动错误", f"程序启动失败:\n{str(e)}")
        except:
            pass
        
        sys.exit(1)
